rust的运算符的重载

一、运算符重载

运算符重载其实不是什么新鲜的东西,而且这个语法其实争议非常大。一些支持这类语法的语言其实也有不少的痛脚在其中。举一个例子,比如c++,左值和右值的问题,本来一个轻松的回答,“等号左面是左值 ,右面的是右值",但由于有了运算符重载,这玩意儿就不敢说对了,是吧?所谓重载,其实就是把常见的允许重新定义其它含义的运算符,用语法层次固定一下。
举一个常见的例子,一头牛和一查树怎么比较大小,没有可比性啊。但是如果我们把牛和树的年龄重新定义为比较大小的含义,那么不就有了可比性么?就酱。没啥!

二、rust中对运算符重载的支持

在rust中,同样支持运算符的重载,不过,它是通过std::ops下的trait来实现的。看一下在官方文档上的说明:
”Overloadable operators.
Implementing these traits allows you to overload certain operators.
Some of these traits are imported by the prelude, so they are available in every Rust program. Only operators backed by traits can be overloaded. For example, the addition operator (+) can be overloaded through the Add trait, but since the assignment operator (=) has no backing trait, there is no way of overloading its semantics. Additionally, this module does not provide any mechanism to create new operators. If traitless overloading or custom operators are required, you should look toward macros or compiler plugins to extend Rust’s syntax.
Implementations of operator traits should be unsurprising in their respective contexts, keeping in mind their usual meanings and operator precedence. For example, when implementing Mul, the operation should have some resemblance to multiplication (and share expected properties like associativity).
Note that the && and || operators short-circuit, i.e., they only evaluate their second operand if it contributes to the result. Since this behavior is not enforceable by traits, && and || are not supported as overloadable operators.
Many of the operators take their operands by value. In non-generic contexts involving built-in types, this is usually not a problem. However, using these operators in generic code, requires some attention if values have to be reused as opposed to letting the operators consume them. One option is to occasionally use clone. Another option is to rely on the types involved providing additional operator implementations for references. For example, for a user-defined type T which is supposed to support addition, it is probably a good idea to have both T and &T implement the traits Add and Add<&T> so that generic code can be written without unnecessary cloning.“

这段话其实主要是说明了重载运算符可以用trait,在一些特殊情况下可以使用宏或者编译器插件方式,并对一些特殊的如||等短路进行了提示,对左值的处理需要使用Clone或者其它相关方式进行处理,另外有一个提醒,重载运算符虽然含义不受限制,但最好是含义相近的,而不是离题万里甚至是相反的。

三、例程

看一下相关的例子:

use std::ops::{Add, Sub};

#[derive(Debug, Copy, Clone, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

impl Add for Point {
    type Output = Self;

    fn add(self, other: Self) -> Self {
        Self {x: self.x + other.x, y: self.y + other.y}
    }
}

impl Sub for Point {
    type Output = Self;

    fn sub(self, other: Self) -> Self {
        Self {x: self.x - other.x, y: self.y - other.y}
    }
}

assert_eq!(Point {x: 3, y: 3}, Point {x: 1, y: 0} + Point {x: 2, y: 3});
assert_eq!(Point {x: -1, y: -3}, Point {x: 1, y: 0} - Point {x: 2, y: 3});

这上面的例子如果初学者可能有点晕,但说明一下就瞬间明白了,在rust中,小写的self表示对象本身,类似于c++中的this,大写的Self,表示对象本身的类型,也就是说类似于c++中this的类型。这时候儿再看上面的代码就清楚很多了吧。type Output是个啥东东,这个东西类似于c++的别名,但和c++略有不同的是,这个还定义了当前重载运算符的结果类型,一定要引起注意。

pub trait Div<Rhs = Self> {
    type Output;
    fn div(self, rhs: Rhs) -> Self::Output;
}

做为trait,运算符重载当然可以做为泛型的限制条件:

use std::ops::Mul;
trait HasArea<T> {
    fn area(&self) -> T;
}
struct Square<T> {
    x: T,
    y: T,
    side: T,
}
impl<T> HasArea<T> for Square<T>
        where T: Mul<Output=T> + Copy {
    fn area(&self) -> T {
        self.side * self.side
    }
}
fn main() {
    let s = Square {
        x: 0.0f64,
        y: 0.0f64,
        side: 12.0f64,
    };
    println!("Area of s: {}", s.area());
}

学习新的技术知识,当然是以技术文档为标准,不过技术文档变化快的,诸如Rust之类,可能就有些头痛,没有办法,新东西,就是这样,适应了就好了。

四、总结

运算符的重载是一把典型的双刃剑,用得好,可以起到事半功倍的效果。否则,还是少用。在实际应用中,c++中曾经有一个小括号重载的例子,对于新手来说,一个是可能迷糊,另外一个,绝大多数IDE不支持这种运算符重载的智能提示和跳转,导致代码阅读的困难。因此,还是要小心谨慎的应用运算符的重载。正所谓,取其长而舍其短,方为上策。
努力吧,归来的少年!
rust的运算符的重载_第1张图片

你可能感兴趣的:(rust,rust,开发语言,后端)