これは、Rustプログラミング言語の第2版で触れられています。ただし、もう少し詳しく見てみましょう。
簡単な例から始めましょう。
特性メソッドを使用するのが適切なのはいつですか?
遅延バインディングを提供する方法は複数あります。
trait MyTrait {
fn hello_word(&self) -> String;
}
または:
struct MyTrait<T> {
t: T,
hello_world: fn(&T) -> String,
}
impl<T> MyTrait<T> {
fn new(t: T, hello_world: fn(&T) -> String) -> MyTrait<T>;
fn hello_world(&self) -> String {
(self.hello_world)(self.t)
}
}
実装/パフォーマンス戦略を無視して、上記の両方の抜粋により、ユーザーは動的な方法hello_world
で動作方法を指定できます。
1つの違い(意味的に)は、trait
実装が、特定の型をT
実装するtrait
場合、hello_world
常に同じ動作をすることを保証するのに対し、struct
実装ではインスタンスごとに異なる動作を許可することです。
メソッドの使用が適切かどうかは、ユースケースに依存します!
関連付けられたタイプを使用するのが適切なのはいつですか?
trait
上記のメソッドと同様に、関連する型はレイトバインディングの形式です(コンパイル時に発生します)。これにより、のユーザーは、trait
特定のインスタンスに対してどの型に置き換えるかを指定できます。それが唯一の方法ではありません(したがって問題です):
trait MyTrait {
type Return;
fn hello_world(&self) -> Self::Return;
}
または:
trait MyTrait<Return> {
fn hello_world(&Self) -> Return;
}
上記のメソッドの遅延バインディングと同等です。
- 最初のものは、与えられたもの
Self
にReturn
関連付けられた単一の
- 二つ目は、代わりに、実現可能
MyTrait
のためのSelf
複数のためにReturn
どの形式がより適切であるかは、単一性を強制することが理にかなっているかどうかによって異なります。例えば:
Deref
unicityがないとコンパイラーは推論中に狂ってしまうため、関連する型を使用します
Add
2つの引数が与えられると論理的な戻り値の型が存在すると考えられているため、関連付けられた型を使用します
ご覧のとおりDeref
、明らかなユースケース(技術的な制約)ですが、のケースAdd
はあまり明確ではありません。i32 + i32
どちらか一方i32
またはComplex<i32>
コンテキストに応じて譲歩することには意味があるのでしょうか?それでも、著者は判断を下して、追加の戻り値の型のオーバーロードは不要であると判断しました。
私の考えでは、正解はありません。それでも、unicityの引数を超えて、関連する型は指定する必要のあるパラメーターの数を減らすため、トレイトの使用を容易にすることを述べます。したがって、通常のトレイトパラメーターを使用する柔軟性の利点が明らかでない場合は、関連するタイプから始めることをお勧めします。
trait/struct MyTrait/MyStruct
1つimpl MyTrait for
またはを許可しますimpl MyStruct
。 汎用であるためtrait MyTrait<Return>
、複数impl
のを使用できます。Return
どのタイプでもかまいません。一般的な構造体は同じです。