我创建了一个两元素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-nightly(ed530d7a3 2015-01-16 22:41:16 +0000)
我不会接受“你只有两个字段,为什么要使用参考”作为答案;如果我想要一个100元素结构怎么办?我会接受一个答案,证明即使有一个大的结构我也应该通过值传递,如果是这样的话(我认为不是这样)。我有兴趣知道结构大小的一个好的经验法则并且通过值vs struct传递,但这不是当前的问题。
答案 0 :(得分:43)
您需要在Add
而不是&Vector
上实施Vector
。
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 实际上,引用类型的特殊之处在于,您可以实现对包中定义的类型的引用的特征(即,如果您允许实现T
的特征,然后你也被允许为&T
实现它。 &mut T
和Box<T>
具有相同的行为,但对于U<T>
,U
未在同一个包中定义,这种情况一般不正确。
答案 1 :(得分:2)
如果要支持所有方案,则必须支持所有组合:
如果是铁锈,this was done through an internal macro。
幸运的是,有一个防锈板条箱impl_os,它也提供了一个宏来为我们编写样板:板条箱提供了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!,如果您的运算符恰好是可交换的,它们也会生成带有相反参数的运算符。