如何为对结构的引用实现 Add 特性?

How do I implement the Add trait for a reference to a struct?

我制作了一个双元素 Vector 结构,我想重载 + 运算符。

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

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

根据我尝试的变体,我要么遇到生命周期问题,要么类型不匹配。具体来说,&self 参数似乎没有被视为正确的类型。

我在 implAdd 上看到了带有模板参数的示例,但它们只会导致不同的错误。

我找到了 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)

我不会接受 "you only have two fields, why use a reference" 作为答案;如果我想要一个 100 元素的结构怎么办?我会接受一个答案,该答案表明即使是大型结构,我也应该按值传递,如果是这样的话(不过我不认为是这样。)我有兴趣了解结构大小的良好经验法则以及按值传递与结构传递,但这不是当前的问题。

您需要在 &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,所以它们也可以实现特征。当trait实现在引用类型上时,self的类型就是引用;引用按值传递。通常,在 Rust 中按值传递意味着转移所有权,但是当按值传递引用时,它们只是被复制(或者 reborrowed/moved 如果它是一个可变引用),并且不会转移所指对象的所有权(因为引用首先不拥有它的引用对象)。考虑到所有这些,Add::add(和许多其他运算符)按值获取 self 是有意义的:如果您需要获取操作数的所有权,您可以在 [= 上实现 Add 47=] 直接,如果你不这样做,你可以在引用上实现 Add

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

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


1 实际上,引用类型的特殊之处在于你可以为你的 crate 中定义的类型的引用实现特征(即如果你被允许为 T,那么您也可以为 &T) 实现它。 &mut TBox<T> 具有相同的行为,但对于 U<T> 通常情况并非如此,其中 U 未在同一个 crate 中定义。

想要支持所有场景,就必须支持所有组合:

  • &T 操作 U
  • T op &U
  • &T 运算 &U
  • T op U

生锈,

幸运的是,有一个 rust crate,impl_ops, that also offers a macro to write that boilerplate for us: the crate offers the 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! 如果您的运算符恰好是可交换的,它还会生成参数相反的运算符。