サンプルコードで身につける: 関連型 — トレイトに型を持たせる
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1最小の関連型 — 実装側が型を1つ決める
trait 側は type Value という「型の置き場所」だけを宣言し、実装側が type Value = String と具体的な型を埋めます。メソッドのシグネチャは Self::Value を通してその型を参照します。まずは「トレイトに型の欄が1つある」というだけの最小形を押さえてください。
2実装ごとに違う型に決まる
同じ Sensor トレイトでも、温度計は f64、ドアセンサーは bool と、実装ごとに読み値の型が1通りに決まります。「この型のセンサーの読み値は必ずこの型」という対応がコードに固定されるのが関連型の本質です。ジェネリクスと違い、1つの型に複数の Reading で重ねて実装することはできません。
3Self::Item を引数と戻り値で使う
関連型はメソッドの引数にも戻り値にも使えます。push する型と pop で返る型が同じ Self::Item で結ばれるため、「入れた型と出てくる型が必ず一致する」ことをトレイトのレベルで保証できます。履歴やキューのようなコンテナ系の抽象でよく使う形です。
4Iterator を自作する — type Item の代表例
標準の Iterator トレイトは type Item を持ち、next が Option<Self::Item> を返します。自作の Countdown に実装すると、for 文など Iterator の機能一式がそのまま使えるようになります。関連型が実際に働いている、もっとも身近な実例です。
5パーサーの出力型を関連型で表す
「何を返すパーサーか」を type Output で表すと、数値パーサーは i32、CSV パーサーは Vec<String> と実装ごとに出力型が決まります。呼び出し側は parse の戻り値の型をシグネチャから正確に知ることができます。変換・読み込み系の抽象化で実務によく現れるパターンです。