web-dev-qa-db-ja.com

関連付けられた型と一般的な型を使用するのが適切な場合

この質問 では、ジェネリック型パラメーターを使用する試みを関連する型に変更することで解決できる問題が発生しました。それが「なぜ関連するタイプがここでより適切なのか」という質問を促し、私はもっと知りたいと思いました。

関連する型を導入したRFC は言う:

このRFCは、以下によって特性のマッチングを明確にします。

  • すべての特性タイプのパラメーターを入力タイプとして扱う
  • 出力タイプである関連タイプを提供します。

RFCは動機付けの例としてグラフ構造を使用しており、これは ドキュメント でも使用されていますが、型パラメーター化されたバージョンよりも関連する型バージョンの利点を完全に評価していないことは認めます。主なことは、distanceメソッドがEdge型を気にする必要がないことです。これは素晴らしいことですが、関連する型を持つ理由が少し浅いと思われます。

関連付けられた型は実際に使用するにはかなり直感的であることがわかりましたが、自分のAPIでそれらを使用する場所とタイミングを決定するときに苦労しています。

コードを書くとき、いつジェネリック型パラメーターよりも関連する型を選択する必要があり、逆の場合はいつですか?

78
Shepmaster

これは の第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実装により、特定のタイプTtraithello_worldは常に同じ動作をしますが、struct実装ではインスタンスごとに異なる動作を許可します。

メソッドの使用が適切かどうかは、ユースケースに依存します!

関連するタイプを使用するのが適切な場合はいつですか?

上記のtraitメソッドと同様に、関連付けられた型はレイトバインディングの形式であり(コンパイル時に発生します)、traitのユーザーは、どの型に置き換えるかを指定できます。それが唯一の方法ではありません(したがって、質問):

trait MyTrait {
    type Return;
    fn hello_world(&self) -> Self::Return;
}

または:

trait MyTrait<Return> {
    fn hello_world(&Self) -> Return;
}

上記のメソッドの遅延バインディングと同等です:

  • 最初のものは、特定のSelfに対して単一のReturnが関連付けられていることを強制します
  • 2番目の方法では、代わりにMyTraitSelfに実装し、複数のReturnを実装できます

どちらの形式がより適切であるかは、ユニシティを実施することが理にかなっているかどうかによって異なります。例えば:

  • Derefは関連付けられた型を使用します。なぜなら、ユニシティがないと、コンパイラは推論中に発狂するからです
  • Addは関連付けられた型を使用します。これは、2つの引数が与えられた場合、論理的な戻り値型があると著者が考えたためです。

ご覧のとおり、Derefは明らかなユースケース(技術的制約)ですが、Addの場合はあまり明確ではありません。多分i32 + i32どちらかを生成するi32またはComplex<i32>コンテキストに応じて?それにもかかわらず、著者は判断を行い、追加のために戻り型をオーバーロードする必要はないと判断しました。

私の個人的なスタンスは、正しい答えはないということです。それでも、ユニシティ引数を超えて、指定する必要のあるパラメーターの数が減るため、関連する型が特性の使用を容易にすることに言及します。したがって、通常の特性パラメーターを使用する柔軟性の利点が明らかでない場合、関連するタイプから始めることをお勧めします。

49
Matthieu M.

関連するタイプはグループ化メカニズムであるため、タイプをグループ化することが理にかなっている場合に使用する必要があります。

ドキュメントで紹介されているGraph特性は、この例です。 Graphをジェネリックにしたいが、特定の種類のGraphを取得したら、NodeまたはEdgeタイプを変えたくないもう。特定のGraphは、単一の実装内でこれらのタイプを変更することを望みません。実際、常に同じであることを望みます。グループ化されているか、associatedと言うこともあります。

27
Steve Klabnik