我制作了一个两元素 Vector
结构,我想重载 +
运算符。
我让我的所有函数和方法都采用引用而不是值,并且我希望 +
运算符以相同的方式工作。
impl Add for Vector {
fn add(&self, other: &Vector) -> Vector {
Vector {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
根据我尝试的变体,我会遇到终身问题或类型不匹配。具体来说,&self
参数似乎没有被视为正确的类型。
我在 impl
和 Add
上看到了带有模板参数的示例,但它们只会导致不同的错误。
我找到了 How can an operator be overloaded for different RHS types and return values?,但即使我将 use std::ops::Mul;
放在顶部,答案中的代码也不起作用。
我每晚使用 rustc 1.0.0 (ed530d7a3 2015-01-16 22:41:16 +0000)
我不会接受“您只有两个字段,为什么要使用参考”作为答案;如果我想要一个 100 个元素的结构怎么办?我会接受一个答案,证明即使有一个大的结构,我也应该按值传递,如果是这样的话(但我不认为是这样。)我有兴趣了解结构大小的一个好的经验法则并通过值与结构传递,但这不是当前的问题。
(%struct.Big* sret, %struct.Big*, %struct.Big*)
。我不声称自己是 LLVM 专家,但看起来它会自动通过引用获取和返回。
%arg7 = alloca %struct.Big, align 8
和 %arg8 = alloca %struct.Big, align 8
),因此至少对于大型结构来说,引用更好。
let c = (&a) + (&b);
会很烦人。
您需要在 &Vector
而不是 Vector
上实施 Add
。
impl<'a, 'b> Add<&'b Vector> for &'a Vector {
type Output = Vector;
fn add(self, other: &'b Vector) -> Vector {
Vector {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
在其定义中,Add::add
始终按值获取 self
。但是引用是与其他任何1 一样的类型,因此它们也可以实现特征。在引用类型上实现特征时,self
的类型是引用;引用是按值传递的。通常,在 Rust 中按值传递意味着转移所有权,但是当按值传递引用时,它们只是被复制(或者如果它是可变引用,则被重新借用/移动),并且不会转移所指对象的所有权(因为引用首先不拥有它的所指对象)。考虑到这一切,Add::add
(和许多其他运算符)按值获取 self
是有意义的:如果您需要获得操作数的所有权,您可以直接在结构/枚举上实现 Add
,如果您不要,您可以在引用上实现 Add
。
这里,self
属于 &'a Vector
类型,因为这是我们在其上实现 Add
的类型。
请注意,我还指定了具有不同生命周期的 RHS
类型参数,以强调两个输入参数的生命周期不相关的事实。
1 实际上,引用类型的特殊之处在于,您可以实现对 crate 中定义的类型的引用的特征(即,如果允许您为 T
实现特征,那么您也可以为 &T
实现它)。 &mut T
和 Box<T>
具有相同的行为,但对于 U<T>
而言,通常情况并非如此,其中 U
未在同一个 crate 中定义。
如果要支持所有场景,则必须支持所有组合:
&T op U
顶部&U
&T op&U
顶部
防锈,this was done through an internal macro。
幸运的是,有一个 rust crate impl_ops,它还提供了一个宏来为我们编写该样板:该 crate 提供了 impl_op_ex! 宏,它生成所有组合。
这是他们的样本:
#[macro_use] extern crate impl_ops;
use std::ops;
impl_op_ex!(+ |a: &DonkeyKong, b: &DonkeyKong| -> i32 { a.bananas + b.bananas });
fn main() {
let total_bananas = &DonkeyKong::new(2) + &DonkeyKong::new(4);
assert_eq!(6, total_bananas);
let total_bananas = &DonkeyKong::new(2) + DonkeyKong::new(4);
assert_eq!(6, total_bananas);
let total_bananas = DonkeyKong::new(2) + &DonkeyKong::new(4);
assert_eq!(6, total_bananas);
let total_bananas = DonkeyKong::new(2) + DonkeyKong::new(4);
assert_eq!(6, total_bananas);
}
更好的是,如果您的运算符恰好是可交换的,它们还有一个 impl_op_ex_commutative! 也会生成参数反转的运算符。