运算符按值重载会导致使用移动值

时间:2015-01-09 09:04:30

标签: copy operator-overloading rust pass-by-value ownership

编译以下使用运算符重载的Rust代码

use std::ops::{Add};

#[derive(Show)]
struct Point {
    x: int,
    y: int
}

impl Add for Point {
    type Output = Point;

    fn add(self, other: Point) -> Point {
        Point {x: self.x + other.x, y: self.y + other.y}
    }
}

fn main() {
    let p: Point = Point {x: 1, y: 0};
    let pp = p + p;
}

由于p:

的所有权导致编译器错误
<anon>:21:18: 21:19 error: use of moved value: `p`
<anon>:21     let pp = p + p;
                           ^
<anon>:21:14: 21:15 note: `p` moved here because it has type `Point`, which is non-copyable
<anon>:21     let pp = p + p;
                       ^

其背后的基本原理被解释here并导致RFC未被接受(部分由于上述示例的原因)。但是,稍后,以下RFC仍然为运算符引入了按值类型的签名。

虽然我理解决定背后的理由。由于我缺乏生锈经验,我不确定允许上述代码工作的“正确”方法是什么(a)如果我不想复制或(b)如何使结构可复制?

2 个答案:

答案 0 :(得分:4)

如果您不想复制,那么就我的新手理解而言,您需要对Add的引用实施Point

这将得到RFC的支持:

  

幸运的是,表达性没有任何损失,因为您始终可以在引用类型上实现特征。但是,对于需要通过引用获取的类型,人体工程学会略有损失,因为您可能需要使用&amp;显式地借用操作数。好处是所有权语义变得更加清晰:它们更类似于正常的函数参数。

确实似乎有效:

use std::ops::{Add};

#[derive(Show)]
struct Point {
    x: i32,
    y: i32
}

impl<'a> Add for &'a Point {
    type Output = Point;

    fn add(self, other: &'a Point) -> Point { //'
        Point {x: self.x + other.x, y: self.y + other.y}
    }
}

fn main() {
    let p: Point = Point {x: 1, y: 0};
    let pp = &p + &p;
    println!("{:?}", pp);
}

playpen

要使Point可复制,只需将#[derive(Show)]替换为#[derive(Show,Copy)]即可。这些结构在默认情况下是可复制的,但changed

答案 1 :(得分:4)

如果您的结构无法复制(例如,它具有Drop实现,无论是本身还是其中一个字段),那么创建多个实现可能是有意义的:值+值,值+引用,参考+值和参考+参考。前三个可以重用其中一个操作数的存储,最后一个可以克隆其中一个操作数,然后只委托给现有的实现。这样,您的库的用户可以轻松决定是否要重用现有值进行优化。

事实上,这就是例如处理BigIntComplex类型。

然而,您的Point可以Copy,因为复制它很便宜。