I'm getting a compilation error from a simple helper method, when using a class with operator overloading. Here is a self-contained test (simplified from my real code, but still demonstrating the problem):
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
}
}
I'm trying to compile this with Rust 1.8, but I'm getting this error:
src/lib.rs:42:5: 42:9 error: cannot move out of borrowed content [E0507]
src/lib.rs:42 self.absolute + self.percent * basis
I've read the Rust Book, and the parts about ownership and borrowing over and over. I've read numerous questions here on StackOverflow about this question, e.g.:
Cannot move out of borrowed content
I don't think my own question is a duplicate because while the error is the same, the circumstances are different. Also if I knew how those other questions applied to this one, I wouldn't have to ask. :-)
So my question is: how can I resolve this error? I don't want to change &self
to self
, because that causes other problems.
Besides just fixing the problem, I would also like to know what Rust is scared of. I don't see any dangers here.
You're implementing the operators on Money
rather than &Money
. This means that the operator will take ownership of its operands. Therefore, in total
, to perform the addition, you'd have to move self.absolute
, which isn't allowed because you can't move out of a borrowed pointer (you can only move values you have ownership of). Rust will copy values if their type implements Copy
(which is the case for primitives like i32
or f64
); otherwise, it will move them, which means that the source will be unusable after the move.
If your Money
struct really only contains a cents
field, I recommend you make it implement Copy
(which also requires implementing Clone
, which would be a good idea to implement even if you don't implement Copy
). You can implement Copy
and Clone
easily with #[derive]
:
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Money {
cents: i64,
}
Now, in total
, instead of moving self.absolute
, Rust will copy it instead. If you can't implement Copy
, then replace self.absolute
with self.absolute.clone()
.
If you had implemented the operators on &Money
, then you could just pass references to your Money
values. For example, with such implementations, total
could be implemented like this:
pub fn total(&self, basis: Money) -> Money {
&self.absolute + &(self.percent * &basis)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With