【问题标题】:"cannot move out of borrowed content" with operator overloading运算符重载时“无法移出借来的内容”
【发布时间】:2016-09-15 18:00:16
【问题描述】:

在使用带有运算符重载的类时,我从一个简单的辅助方法中得到一个编译错误。这是一个独立的测试(根据我的真实代码进行了简化,但仍然证明了问题):

use std::ops::{Add, Sub, Neg, Mul, Div};

#[derive(Debug, Eq, PartialEq)]
pub struct Money {
    cents: i64,
}
impl Money {
    pub fn new(cents: i64) -> Money {
        Money { cents: cents }
    }
}
impl Add for Money {
    type Output = Money;
    fn add(self, other: Money) -> Money {
        Money { cents: self.cents + other.cents }
    }
}
impl Mul<Money> for f64 {
    type Output = Money;
    fn mul(self, rhs: Money) -> Money {
        Money { cents: (self * rhs.cents as f64) as i64 }
    }
}

#[derive(Debug)]
pub struct AbsOrPerc {
    pub absolute: Money,
    pub percent: f64,
}
impl AbsOrPerc {
    pub fn new(abs: Money, perc: f64) -> AbsOrPerc {
        AbsOrPerc {
            absolute: abs,
            percent: perc,
        }
    }

    pub fn total(&self, basis: Money) -> Money {
        // This works:
        // Money::new((self.absolute.cents as f64 + self.percent * basis.cents as f64) as i64)
        // This doesn't:
        self.absolute + self.percent * basis
    }
}

我正在尝试用 Rust 1.8 编译它,但我收到了这个错误:

src/lib.rs:42:5: 42:9 error: cannot move out of borrowed content [E0507]
src/lib.rs:42     self.absolute + self.percent * basis

我一遍又一遍地阅读了 Rust Book,以及关于所有权和借贷的部分。我在 StackOverflow 上阅读了很多关于这个问题的问题,例如:

Cannot move out of borrowed content

我不认为我自己的问题是重复的,因为虽然错误相同,但情况不同。此外,如果我知道这些其他问题如何适用于这个问题,我就不必问了。 :-)

所以我的问题是:我该如何解决这个错误?我不想将&amp;self 更改为self,因为这会导致其他问题。

除了解决问题,我还想知道 Rust 害怕什么。我认为这里没有任何危险。

【问题讨论】:

标签: rust


【解决方案1】:

您在 Money 而不是 &amp;Money 上实现运算符。这意味着操作员将取得其操作数的所有权。因此,在total 中,要执行加法,您必须移动self.absolute,这是不允许的,因为您不能移出借来的指针(您只能移动您拥有所有权的值)。如果它们的类型实现了Copy,Rust 将复制值(对于像i32f64 这样的原语就是这种情况);否则,它将移动它们,这意味着移动后源将无法使用。

如果你的Money 结构真的只包含一个cents 字段,我建议你让它实现Copy(这也需要实现Clone,即使你不这样做也是一个好主意实施Copy)。您可以使用#[derive] 轻松实现CopyClone

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Money {
    cents: i64,
}

现在,在 total 中,Rust 不会移动 self.absolute,而是复制它。如果无法实现Copy,则将self.absolute替换为self.absolute.clone()


如果您在&amp;Money 上实现了运算符,那么您可以只传递对Money 值的引用。例如,通过这样的实现,total 可以这样实现:

pub fn total(&self, basis: Money) -> Money {
    &self.absolute + &(self.percent * &basis)
}

【讨论】:

  • 可能值得评论所有权和Add 按价值消耗加数的事实,甚至可能涉及为什么这样的东西有用以及为什么在参考上实施Add 会允许某人避免在需要时实施Copy
  • 我认为值得回忆一下 Rust 是 默认移动(在 args 中就像在 assignments 中一样),这对于新手来说通常是陌生的,有一个参考。很好的答案。
  • 感谢您的出色回答。复制/移动/借用正在慢慢下沉。这个答案很有帮助!同样在教学上,二进制文件更容易理解,据我所知,这本书从来没有真正将它们呈现为(复制 vs 移动)vs 借用——它似乎总是将两者进行对比,而没有提及第三个。将这三者放在一起非常有帮助。
猜你喜欢
  • 2019-08-29
  • 2018-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-20
  • 1970-01-01
  • 1970-01-01
  • 2015-03-25
相关资源
最近更新 更多