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]
。因为 $a2
中 3
,4
,5
对应的下标是 0
,1
,2
,而这几个下标在 $a1
中是已存在的。因此忽略。只将元素 6
,7
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]))
}
完整源代码参考这里