Rustにおけるトレイトはインターフェースを定義する手段であり、型が実装しなければならないメソッドのセットを記述します。トレイトの実装により、ジェネリクスやメソッドの動的ディスパッチを使用できます。
静的ディスパッチ(ジェネリクスとバウンドを通じて):
メソッドの具体的な実装がコンパイル時に呼び出されます。一般化されたコード(impl<T: Trait>またはfn foo<T: Trait>(t: T))は、コンパイル時に各型のために個別の関数バージョン(モノモルフィズム)を生成します。
動的ディスパッチ:
関数がトレイトへの参照としてオブジェクトを受け取る場合(&dyn Trait)、コンパイラはどの型が使用されるかを事前に知りません。メソッドの呼び出しは実行時にvtable(仮想テーブル)を介して行われます。
例:
trait Animal { fn speak(&self); } struct Dog; impl Animal for Dog { fn speak(&self) { println!("Woof!"); } } // 静的ディスパッチ def print_animal_static<T: Animal>(a: T) { a.speak(); } // 動的ディスパッチ def print_animal_dyn(a: &dyn Animal) { a.speak(); }
使用する場合:
ジェネリック型のトレイトをVec<Trait>に格納することはできますか?なぜですか?
回答:
いいえ、それはできません。Vecはサイズが決まった型を要求し、トレイト自体にはサイズがありません。Box<dyn Trait>またはトレイトへの参照を使用する必要があります。
// let v: Vec<Trait> = vec![]; // コンパイルエラー let v: Vec<Box<dyn Animal>> = vec![Box::new(Dog)]; // 正しい
物語
BoxまたはRcを使用せずにトレイトオブジェクトのコレクションを作成しようとした例:
// let coll: Vec<MyTrait> = vec![]; // エラー: the size for values of type ... cannot be known
物語
物語
デモクライアントで、さまざまなプロトコルとの相互作用のために一時的なオブジェクトへの参照(&dyn Trait)を保存しようとし、ダングリング参照やランタイムパニックが発生しました。明示的な所有権を持つBox<dyn Trait>に移行する必要がありました。