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]
。因為 $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]))
}
完整原始碼參考這裡