Rust 学习之运算符重载

Rust 学习之运算符重载

最近一直在微信读书上阅读《深入浅出 Rust》,因为一直在地铁上阅读,导致没办法在阅读到的知识点立即验证和实践,从而阅读效果不佳。借着此次有时间,记录一下其中的运算符重载。

关于运算符重载,在《Rust 编程语言》中没有找到相关章节,但在《Rust 基础》(英文名是 RustPrimer)中找到了相关章节

所谓运算符,百度的定义如下:

运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。

通俗的讲,就是自定义一些运算符的功能,使代码看起来支持一些特殊数据类型的运算。

此外,《通过例子学 Rust》的描述感觉更易懂:

在 Rust 中,很多运算符可以通过 trait 来重载。也就是说,这些运算符可以根据它们的 输入参数来完成不同的任务。这之所以可行,是因为运算符就是方法调用的语法糖。例 如,a + b 中的 + 运算符会调用 add 方法(也就是 a.add(b))。这个 add 方 法是 Add trait 的一部分。因此,+ 运算符可以被任何 Add trait 的实现者使用。

首先我们通过 Rust 标准库文档的例子入门:

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

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

impl Add for Point {
    type Output = Point;

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

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_point_add() {
        assert_eq!(Point {x: 3, y: 3} + Point {x: 4, y: 4}, Point {x: 7, y: 7});
    }
}

上面的代码中,声明了一个新的类型 Point,随后,为这个类型实现了 Add trait,这样,在一个表达式中,对 Point 类型的数据进行了 + 运算时,就会调用 trait 中的 add 方法,实现 Point 类型的数据的相加。

模拟实现 PHP 中数组的 +

因为最熟悉的语言是 PHP,对 PHP 中数组的 + 运算尤为深刻。所以打算对 Rust 数组也实现类似的功能。

PHP 中两个数组相加,有点类似于 merge,但跟 merge 又有所区别:如果第一个数组中的某个元素下标已存在,则会忽略第二个数组中相同下标的元素;如果下标 a 在第 1 个数组中不存在,则将第 2 个数组中该下标对应的元素 merge 到新数组中。比如:

$a1  = [1,2,3];
$a2 = [3,4,5,6,7];
var_dump($a1 + $a2);

$a1$a2 相加后,最终的结果是:[1, 2, 3, 6, 7]。因为 $a2345 对应的下标是 012,而这几个下标在 $a1 中是已存在的。因此忽略。只将元素 67 merge 进新数组。

我们再看下 Rust 中数组相加默认是什么行为:

#[test]
fn test_vec_plus() {
    let a1: Vec<u32> = vec![1,2,3];
    let a2: Vec<u32> = vec![1,2,4,5,6];
    let a3 = a1 + a2;
    println!("{}-{}", a1, a3)
    assert!(false);
}

运行测试用例 cargo t -- notes::op_rhs::tests

error[E0369]: cannot add `std::vec::Vec<u32>` to `std::vec::Vec<u32>`
  --> src/notes/op_rhs.rs:33:21
   |
33 |         let a3 = a1 + a2;
   |                  -- ^ -- std::vec::Vec<u32>
   |                  |
   |                  std::vec::Vec<u32>
   |
   = note: an implementation of `std::ops::Add` might be missing for `std::vec::Vec<u32>`

很遗憾,Rust 默认对 Vec<i32> 类型没有实现 + 的运算符重载。那我们尝试自己手动实现吧。

由于 Rust 的 Orphan 规则,我们不能对标准库中的 Vec 实现 Add trait,因此,必须得用自己定义的类型包装一下:

#[derive(Debug, PartialEq)]
struct MyVec(Vec<u32>);

针对 MyVec 类型 Add trait,即实现 add 方法:

fn add(self, other: MyVec) -> MyVec {
    if self.0.is_empty() {
        return other;
    }
    let mut v3: Vec<u32> = Vec::new();
    self.0.iter().for_each(|x| v3.push(*x));
    let len1 = self.0.len();
    let len2 = other.0.len();
    if len1 < len2 {
        for i in len1..len2 {
            v3.push(other.0[i]);
        }
    }
    MyVec(v3)
}

最后,写一个测试用例:

#[test]
fn test_vec_plus() {
    let a1: MyVec = MyVec(vec![1,2,3]);
    let a2: MyVec = MyVec(vec![1,2,4,5,6]);
    let a3: MyVec = a1 + a2;
    assert_eq!(a3, MyVec(vec![1,2,3,5,6]))
}

完整源代码参考这里

参考资料

posted @ 2020-09-19 15:59  suhanyujie  阅读(1264)  评论(0编辑  收藏  举报