所有権を持つトレイトオブジェクト
前に、トレイトオブジェクトを参照とともに使用する方法、たとえば &dyn Pet を見ました。 しかし、Box のようなスマートポインタとトレイトオブジェクトを組み合わせて使用し、 所有権を持つトレイトオブジェクト Box<dyn Pet> を作ることもできます。
// 著作権 2024 Google LLC // SPDX-License-Identifier: Apache-2.0 struct Dog { name: String, age: i8, } struct Cat { lives: i8, } trait Pet { fn talk(&self) -> String; } impl Pet for Dog { fn talk(&self) -> String { format!("ワン、私の名前は{}です!", self.name) } } impl Pet for Cat { fn talk(&self) -> String { String::from("ニャー!") } } fn main() { let pets: Vec<Box<dyn Pet>> = vec![ Box::new(Cat { lives: 9 }), Box::new(Dog { name: String::from("Fido"), age: 5 }), ]; for pet in pets { println!("こんにちは、あなたは誰ですか? {}", pet.talk()); } }
pets を割り当てた後のメモリレイアウト:
- 特定のトレイトを実装する型は、それぞれサイズが異なる場合があります。これにより、 上の例の
Vec<dyn Pet>のようなものを持つことはできません。 dyn Petは、Petを実装する動的サイズ型についてコンパイラに伝える 方法です。- この例では、
petsはスタックに割り当てられ、ベクタのデータはヒープ上に あります。2 つのベクタ要素は ファットポインタ です:- ファットポインタは、幅が 2 倍のポインタです。これには 2 つの構成要素が あります。実際のオブジェクトへのポインタと、その特定のオブジェクトに対する
Pet実装の virtual method table(vtable)へのポインタです。 Fidoという名前のDogのデータは、nameフィールドとageフィールドです。Catにはlivesフィールドがあります。
- ファットポインタは、幅が 2 倍のポインタです。これには 2 つの構成要素が あります。実際のオブジェクトへのポインタと、その特定のオブジェクトに対する
- 上の例で、次の出力を比較してください:
// 著作権 2024 Google LLC // SPDX-License-Identifier: Apache-2.0 println!("{} {}", std::mem::size_of::<Dog>(), std::mem::size_of::<Cat>()); println!("{} {}", std::mem::size_of::<&Dog>(), std::mem::size_of::<&Cat>()); println!("{}", std::mem::size_of::<&dyn Pet>()); println!("{}", std::mem::size_of::<Box<dyn Pet>>());