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

时间:2015-01-17 22:40:51

标签: reference rust traits lifetime

我创建了一个两元素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传递,但这不是当前的问题。

2 个答案:

答案 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 TBox<T>具有相同的行为,但对于U<T>U未在同一个包中定义,这种情况一般不正确。

答案 1 :(得分:2)

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

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

如果是铁锈,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!,如果您的运算符恰好是可交换的,它们也会生成带有相反参数的运算符。