最近在實作computation books第九章,用到很多Rust運算子重載的部分。
運算子重載嘛,可以對自己定義的struct 或是enum 使用運算子,這樣就能寫出 Vec3 + Vec3 這樣比較漂亮的寫法,不用 Vec3.add(Vec3),
啊雖然兩個本質上沒什麼兩樣啦
這章定義一個Sign的類別,分為
- 正
- 負
- 零
- 未知
我們要實作他的乘法和加法:
enum Sign { POSITIVE, NEGATIVE, ZERO, UNKNOWN, }
Rust可重載的運算子請參考 ops 的文件
。
另外是比較運算子 cmp
,包括 PartialEq 跟 PartialOrd:
例如我們要重載乘法運算子,以下是網站上的定義:
pub trait Mul<RHS = Self> {
type Output;
fn mul(self, rhs: RHS) -> Self::Output;
}
實作時當然就是先以use這個trait,然後實作這trait並加入相關的函式:
use std::ops::Mul;
impl Mul for Sign {
type Output = Sign;
fn mul(self, rhs: Self) -> Self {
if self == Sign::ZERO || rhs == Sign::ZERO {
Sign::ZERO
} else if self == Sign::UNKNOWN || rhs == Sign::UNKNOWN {
Sign::UNKNOWN
} else if self == rhs {
Sign::POSITIVE
} else {
Sign::NEGATIVE
}
}
}
這樣就完成了,現在我們就能這樣寫了:
assert_eq!(Sign::NEGATIVE, Sign::POSITIVE * Sign::NEGATIVE);
另外常遇到的問題是,將運算子重載和Rust 的泛型一起用時,例如,我們定義 sum_of_square
這個 function,並希望使用泛型:
fn inner_product<T: Copy>(lhs: T, rhs: T) -> T {
lhs*lhs + rhs*rhs
}
這樣編譯並不會過,因為泛型 T 並不適用乘法跟加法,我們需要告訴編譯器,只有實作Mul跟Add trait的型別才能通過。 同時指定 Output 型別同樣為T,文件上並沒有講如何實作這部分,我忘記是在哪找到要這樣寫的,就為了那個Output=T花了我超多時間RRRRR,反正Rust 的文件就是這樣…
fn inner_product<T: Mul<T, Output=T>+Add<T, Output=T>+Copy> -> T {
lhs*lhs + rhs*rhs
}
如果你要不同的實作,例如我要能夠跟 i32 相加,那就是:
fn foo<T: Add<i32, Output=T>>(x: T) -> T {
x+1
}
個人覺得:相較之下,C++運算子重載的語法真的相當的複雜(事實上我覺得我已經不會寫了Orz),rust 簡潔不少,使用trait 中的function name來實作也比C++ 用 operator+, operator* 好讀很多。
有關於文中所提 Sign 的實作,請參考 Github