dyn
を利用してトレイトを返す
Rustのコンパイラはあらゆる関数のリターン型に必要なスペースを知っておく必要があります。
つまり、すべての関数は具体的な型を返す必要があるのです。
他の言語と違って、Animal
のようなトレイトがある場合に、Animal
を返す関数を書くことはできません。
なぜなら、そのトレイトの異なる実装はそれぞれ別の量のメモリを必要とするからです。
しかし、簡単な回避策があります。
直接トレイトオブジェクトを返す代わりに、Animal
を 含む Box
を返すのです。
Box
はヒープ中のメモリへの単なる参照です。
参照のサイズは静的に知ることができ、コンパイラは参照がヒープに割り当てられたAnimal
を指していると保証できるので、私たちは関数からトレイトを返すことができます。
ヒープにメモリを割り当てる際、Rustは可能な限り明示的であろうとします。
なので、もしあなたの関数がヒープ上のトレイトへのポインタを返す場合、例えばBox<dyn Animal>
のように、リターン型にdyn
キーワードをつける必要があります。
struct Sheep {} struct Cow {} trait Animal { // Instance method signature // インスタンスのメソッドシグネチャ fn noise(&self) -> &'static str; } // Implement the `Animal` trait for `Sheep`. // `Sheep`に`Animal`トレイトを実装する。 impl Animal for Sheep { fn noise(&self) -> &'static str { "baaaaah!" } } // Implement the `Animal` trait for `Cow`. // `Cow`に`Animal`トレイトを実装する。 impl Animal for Cow { fn noise(&self) -> &'static str { "moooooo!" } } // Returns some struct that implements Animal, but we don't know which one at compile time. // Animalを実装した何らかの構造体を返す。 // ただし、コンパイル時にはどの実装か分からない。 fn random_animal(random_number: f64) -> Box<dyn Animal> { if random_number < 0.5 { Box::new(Sheep {}) } else { Box::new(Cow {}) } } fn main() { let random_number = 0.234; let animal = random_animal(random_number); println!("You've randomly chosen an animal, and it says {}", animal.noise()); }