如何实现添加特征以引用结构?

Jer*_*sen 40 reference traits lifetime rust

我做了一个两元素Vector结构,我想重载+运算符.

我使我的所有函数和方法都采用引用而不是值,我希望+运算符以相同的方式工作.

impl Add for Vector {
    fn add(&self, other: &Vector) -> Vector {
        Vector {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

根据我尝试的变化,我要么遇到生命问题,要么输入不匹配.具体来说,这个&self论点似乎没有被视为正确的类型.

我已经看到了模板参数的例子上impl,以及Add,但他们只是导致不同的错误.

我发现如何为不同的RHS类型和返回值重载运算符?但即使我把一个use std::ops::Mul;放在顶部,答案中的代码也不起作用.

我正在使用rustc 1.0.0-nightly(ed530d7a3 2015-01-16 22:41:16 +0000)

我不接受"你只有两个字段,为什么要使用参考"作为答案; 如果我想要一个100元素结构怎么办?我会接受一个答案,证明即使有一个大的结构我也应该通过值传递,如果是这样的话(我认为不是这样).我有兴趣知道结构大小的一个好的经验法则并且通过值vs struct传递,但这不是当前的问题.

Fra*_*gné 53

你需要实现Addon &Vector而不是on 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,
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在其定义中,Add::add总是self取值.但引用类似于任何其他类型1,因此它们也可以实现特征.当在引用类型上实现特征时,类型self是引用; 引用按值传递.通常,在Rust中传递值意味着转移所有权,但是当引用按值传递时,它们只是被复制(或者如果它是可变引用则被重新借用/移动),并且不会转移引用的所有权(因为引用)首先不拥有它的指示物).考虑到所有这些,Add::add(以及许多其他运算符)self按值获取是有意义的:如果您需要获取操作数的所有权,则可以实现Add直接在结构/枚举上,如果不是,可以Add在引用上实现.

这里self是类型&'a Vector,因为这是我们正在实现的类型Add.

请注意,我还指定了RHS具有不同生命周期的type参数,以强调两个输入参数的生命周期不相关的事实.


1实际上,引用类型的特殊之处在于,您可以实现对包中定义的类型的引用的特征(即,如果您被允许实现特征T,那么您也可以实现它&T).&mut T并且Box<T>具有相同的行为,但是对于未在同一个箱中定义的U<T>地方而言,这通常不正确U.

  • 哇.惊讶于这是正确的答案,但它确实如此.这一切都非常违反直觉.你可以用两种不同的方式定义Add,具体取决于它是否是一个引用或者不是一个麻烦的配方. (5认同)
  • "Add :: add总是以值为自我.这里,self是类型&'a Vector,因为这是我们实现Add的类型." 这是关键信息,自我类型的变化取决于特征是否供参考.谢谢! (3认同)
  • 实际上,如果一个是 ref,另一个不是,您甚至需要实现所有组合,反之亦然。在 Rust 中,它是通过宏完成的... /sf/ask/2716797121/ (2认同)

Emm*_*ery 6

如果要支持所有方案,则必须支持所有组合:

  • &T op U
  • op
  • &T op&U
  • op

在防锈方面,这是通过内部宏完成的

幸运的是,有一个防锈板条箱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);
}
Run Code Online (Sandbox Code Playgroud)

更好的是,它们具有impl_op_ex_commutative!如果您的运算符恰好是可交换的,那么也会生成带有相反参数的运算符。