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

zge*_*erd 7 copy operator-overloading pass-by-value ownership rust

编译以下使用运算符重载的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;
}
Run Code Online (Sandbox Code Playgroud)

由于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;
                       ^
Run Code Online (Sandbox Code Playgroud)

这里解释了它背后的基本原理,并导致了一个未被接受的RFC(由于上述例子的原因而导致的一部分).但是,后面的RFC仍然为运营商引入了按值类型的签名.

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

Mic*_*ski 5

如果您不想复制,那么就我的新手理解而言,您需要实现AddPoint.

这将得到 RFC 的支持:

幸运的是,表达能力没有损失,因为您始终可以在引用类型上实现 trait。但是,对于确实需要引用的类型,在人体工程学方面会略有损失,因为您可能需要使用 & 显式借用操作数。好处是所有权语义变得更加清晰:它们更类似于普通的函数参数。

确实它似乎有效:

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);
}
Run Code Online (Sandbox Code Playgroud)

(游戏围栏

Point改为可复制,只需替换#[derive(Show)]#[derive(Show,Copy)]. 默认情况下,此类结构曾经是可复制的,但它更改了.


Vla*_*eev 5

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

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

Point然而,你的可以直接制作Copy,因为复制它很便宜。