この質問 では、ジェネリック型パラメーターを使用する試みを関連する型に変更することで解決できる問題が発生しました。それが「なぜ関連するタイプがここでより適切なのか」という質問を促し、私はもっと知りたいと思いました。
関連する型を導入したRFC は言う:
このRFCは、以下によって特性のマッチングを明確にします。
- すべての特性タイプのパラメーターを入力タイプとして扱う
- 出力タイプである関連タイプを提供します。
RFCは動機付けの例としてグラフ構造を使用しており、これは ドキュメント でも使用されていますが、型パラメーター化されたバージョンよりも関連する型バージョンの利点を完全に評価していないことは認めます。主なことは、distance
メソッドがEdge
型を気にする必要がないことです。これは素晴らしいことですが、関連する型を持つ理由が少し浅いと思われます。
関連付けられた型は実際に使用するにはかなり直感的であることがわかりましたが、自分のAPIでそれらを使用する場所とタイミングを決定するときに苦労しています。
コードを書くとき、いつジェネリック型パラメーターよりも関連する型を選択する必要があり、逆の場合はいつですか?
これは の第2版Rustプログラミング言語 で触れられています。 、もう少し詳しく見ていきましょう。
より簡単な例から始めましょう。
いつ形質メソッドを使用するのが適切ですか?
遅延バインディングを提供する方法は複数あります:
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
は関連付けられた型を使用します。なぜなら、ユニシティがないと、コンパイラは推論中に発狂するからですAdd
は関連付けられた型を使用します。これは、2つの引数が与えられた場合、論理的な戻り値型があると著者が考えたためです。ご覧のとおり、Deref
は明らかなユースケース(技術的制約)ですが、Add
の場合はあまり明確ではありません。多分i32 + i32
どちらかを生成するi32
またはComplex<i32>
コンテキストに応じて?それにもかかわらず、著者は判断を行い、追加のために戻り型をオーバーロードする必要はないと判断しました。
私の個人的なスタンスは、正しい答えはないということです。それでも、ユニシティ引数を超えて、指定する必要のあるパラメーターの数が減るため、関連する型が特性の使用を容易にすることに言及します。したがって、通常の特性パラメーターを使用する柔軟性の利点が明らかでない場合、関連するタイプから始めることをお勧めします。
関連するタイプはグループ化メカニズムであるため、タイプをグループ化することが理にかなっている場合に使用する必要があります。
ドキュメントで紹介されているGraph
特性は、この例です。 Graph
をジェネリックにしたいが、特定の種類のGraph
を取得したら、Node
またはEdge
タイプを変えたくないもう。特定のGraph
は、単一の実装内でこれらのタイプを変更することを望みません。実際、常に同じであることを望みます。グループ化されているか、associatedと言うこともあります。