2016-05-19 2 views
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 
    } 
} 

私は錆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 

私は錆書と所有権と借用に関する部分を何度も読んだことがあります。私はこの質問についてStackOverflowの上ここでは、多くの質問、例えば:

Cannot move out of borrowed content

を読んだ私は、エラーが同じであるが、状況が異なっているので、自分の質問が重複しているとは思いません。また、私はこれらの他の質問がこの質問にどのように適用されたかを知っていれば、私は尋ねる必要はありません。 :-)

私の質問は:どのように私はこのエラーを解決できますか? &selfからselfに変更したくない場合は、他の問題が発生します。

問題を解決するだけでなく、私はRustが何を怖がっているかも知りたいと思います。私はここにどんな危険も見ない。

+0

はhttp://stackoverflow.com/q/28595075/155423、http://stackoverflow.com/q/29926724/155423も参照してください、 http://stackoverflow.com/q/30974593/155423、http://stackoverflow.com/q/28527702/155423、http://stackoverflow.com/q/34621969/155423、およびhttp://stackoverflow.com/q/28843931/155423。 – Shepmaster

答えて

8

&Moneyではなく、Moneyに演算子を実装しています。これは、オペレータがのオペランドの所有権をにすることを意味します。したがって、totalでは、加算を実行するには、self.absoluteを移動する必要があります。これは、借用したポインタから移動できないためです(所有権のある値のみ移動できます)。彼らのタイプがCopyi32またはf64のようなプリミティブの場合)を実装している場合、錆はコピーの値になります。それ以外の場合は、に移動します。移動後はソースが使用できなくなります。

あなたMoney構造体には本当に唯一centsフィールドが含まれている場合、私はあなたが、それは(も、あなたがCopyを実装していない場合でも実装することをお勧めされるであろう、Cloneを実装する必要がある)Copyを実装しますお勧めします。あなたは#[derive]で簡単CopyCloneを実装することができます。今すぐ

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

totalで、代わりにself.absoluteを移動する、錆ではなく、それをコピーします。 Copyを実装できない場合はself.absoluteself.absolute.clone()に置き換えてください。


あなたは&Money上の演算子を実装していた場合、あなたは自分のMoney値への参照を渡すことができます。例えば、そのような実装で、totalはこのように実装することができます

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

所有権と 'Add'が加算値を値で消費し、なぜそのようなことが有用で、なぜリファレンスに' Add'を実装すると誰かが 'Copy'を実装するのを避けることができるという事実にコメントする価値がありますif彼らは必要でした。 – Shepmaster

+0

私はRustがデフォルトでは_moveであることを思い出す価値があると思います。いい答えだ。 –

+0

この素晴らしい答えをありがとう。コピー/移動/借りがゆっくりと沈んでいます。この回答は多くの助けになります!教育的にも、バイナリは分かりやすく、私の知る限りの本は本当にそれらを(Copy vs Move)vs Borrowと表示します。それは常に2つを対照し、3つ目は言わないようです。 3つすべてをまとめると非常に便利です。 –

関連する問題