web-dev-qa-db-ja.com

構造体への参照にAddトレイトを実装するにはどうすればよいですか?

2つの要素Vector構造体を作成し、+演算子をオーバーロードしたいと思います。

私はすべての関数とメソッドが値ではなく参照をとるようにし、+演算子が同じように機能するようにしました。

impl Add for Vector {
    fn add(&self, other: &Vector) -> Vector {
        Vector {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}

私が試すバリエーションに応じて、寿命の問題またはタイプの不一致が発生します。具体的には、&self引数が正しい型として扱われないようです。

implAddのテンプレート引数を使用した例を見ましたが、それらは異なるエラーを発生させるだけです。

私は さまざまなRHSタイプと戻り値に対して演算子をどのようにオーバーロードすることができますか? を見つけましたが、use std::ops::Mul;を先頭に配置しても、回答のコードは機能しません。

私はrustc 1.0.0-nightlyを使用しています(ed530d7a3 2015-01-16 22:41:16 +0000)

「フィールドが2つしかないのに、なぜ参照を使用するのか」という答えは受け入れません。 100要素の構造体が必要な場合はどうなりますか?構造体が大きい場合でも、値で渡す必要があることを示す回答を受け入れます(その場合はそうですが、そうではないと思います)。構造体のサイズに関する大まかな経験則に興味があります。値と構造体の受け渡しですが、これは現在の問題ではありません。

49
Jeremy Sorensen

Addではなく&VectorVectorを実装する必要があります。

impl<'a, 'b> Add<&'b Vector> for &'a Vector {
    type Output = Vector;

    fn add(self, other: &'b Vector) -> Vector {
        Vector {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}

その定義では、Add::addは常に値によってselfを取ります。しかし、参照は他のようなタイプです1なので、特性も実装できます。特性が参照型に実装されている場合、selfの型は参照です。参照は値で渡されます。通常、Rustで値で渡すことは所有権の譲渡を意味しますが、参照が値で渡されると、それらは単にコピー(または変更可能な参照の場合は再借用/移動)され、それは行われません。参照の所有権を転送します(参照が最初に参照を所有していないため)。これらすべてを考慮すると、Add::add(および他の多くの演算子)が値によってselfを取得することは理にかなっています。オペランドの場合、構造体/列挙型にAddを直接実装できます。実装しない場合は、参照にAddを実装できます。

ここで、self&'a Vector型です。これは、Addを実装している型だからです。

2つの入力パラメーターの存続期間が無関係であることを強調するために、異なる存続期間でRHS型パラメーターも指定したことに注意してください。


1 実際、参照タイプは、クレートで定義されたタイプへの参照の特性を実装できるという点で特別です(つまり、Tの特性を実装することが許可されている場合、&Tの実装も許可されています)。 &mut TBox<T>は同じ動作をしますが、Uが同じクレートで定義されていないU<T>の場合は一般的に正しくありません。

48
Francis Gagné

すべてのシナリオをサポートする場合は、すべての組み合わせをサポートする必要があります。

  • &T op U
  • T op&U
  • &T op&U
  • T op U

Rust proper、 これは内部マクロを通じて行われた で。

ありがたいことに、Rust crate、 impl_os があります。これは、ボイラープレートを記述するマクロも提供します。クレートは impl_op_ex! を提供します=すべての組み合わせを生成するマクロ。

以下がそのサンプルです。

#[macro_use] extern crate impl_ops;
use std::ops;

impl_op_ex!(+ |a: &DonkeyKong, b: &DonkeyKong| -> i32 { a.bananas + b.bananas });

fn main() {
    let total_bananas = &DonkeyKong::new(2) + &DonkeyKong::new(4);
    assert_eq!(6, total_bananas);
    let total_bananas = &DonkeyKong::new(2) + DonkeyKong::new(4);
    assert_eq!(6, total_bananas);
    let total_bananas = DonkeyKong::new(2) + &DonkeyKong::new(4);
    assert_eq!(6, total_bananas);
    let total_bananas = DonkeyKong::new(2) + DonkeyKong::new(4);
    assert_eq!(6, total_bananas);
}

さらに良いことに、それらには impl_op_ex_commutative! があり、演算子が可換である場合、パラメーターを逆にした演算子も生成します。

3