首页 文章

运营商超载“无法摆脱借来的内容”

提问于
浏览
8

当使用具有运算符重载的类时,我从简单的辅助方法获得编译错误 . 这是一个独立的测试(从我的真实代码简化,但仍然证明了问题):

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

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

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

除了解决问题之外,我还想知道Rust害怕什么 . 我没有看到任何危险 .

1 回答

  • 8

    您正在 Money 而不是 &Money 上实施运算符 . 这意味着运营商将获得其操作数的所有权 . 因此,在 total 中,要执行添加,您必须移动 self.absolute ,这不是一个借来的指针(您只能移动您拥有的值) . 如果它们的类型实现Copy(对于像 i32f64 这样的基元的情况),Rust将复制值;否则,它会移动它们,这意味着移动后源将无法使用 .

    如果您的 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() .


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

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

相关问题