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

9
编译以下使用运算符重载的 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;
                       ^

这里解释了它的原理,可以点此链接。这导致了一个 RFC 的提出,但由于上述示例的原因,该提案并未被接受。然而,后来的 RFC 仍引入了按值传递类型签名来实现操作符。
尽管我理解做出这个决定的原理,但由于我对 Rust 缺乏经验,我不确定允许上述代码工作的“正确”方法是(a)如果我不想复制代码或(b)如何使结构体可复制?
2个回答

6
如果您不想进行复制操作,则根据我的新手理解,您需要在指向Point的引用上实现Add。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);
}

(playpen)

要使 Point 可复制,只需将 #[derive(Show)] 替换为 #[derive(Show,Copy)]。这样的结构在以前是默认可复制的,但是它已经发生了变化


1
这个问题在于 let pp = &p + &p + &p 不起作用。 - SirVer
@SirVer 是的,你需要写类似 let pp = &(&p + &p) + &p 的东西。我猜实际操作中最好的方法是创建几个实现,就像 Vladimir Matveev 建议的那样(或者只需派生 Copy 就可以了)。 - Michał Politowski

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

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

但是,您的Point可以直接成为Copy,因为它很容易复制。


您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - SirVer
1
我不确定我理解你的问题。如果你的类型复制起来很昂贵,那就不要让它实现Copy,而是为四个变体实现操作特性(self+self、&self+self、self+&self、&self+&self)。如果你的类型太大了,你无论如何都需要把数据放在堆上,所以移动语义将确保只有结构本身被复制。 - Vladimir Matveev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接