dynを利用してトレイトを返す
Rustのコンパイラはあらゆる関数のリターン型に必要なスペースを知っておく必要があります。つまり、すべての関数は具体的な型を返す必要があるのです。他の言語と違って、Animalのようなトレイトがある場合に、Animalを返す関数を書くことはできません。なぜなら、そのトレイトの異なる実装はそれぞれ別の量のメモリを必要とするからです。
しかし、簡単な回避策があります。直接トレイトオブジェクトを返す代わりに、Animalを 含む Boxを返すのです。Boxはヒープ中のメモリへの単なる参照です。参照のサイズは静的に知ることができ、コンパイラは参照がヒープに割り当てられたAnimalを指していると保証できるので、私たちは関数からトレイトを返すことができます。
ヒープにメモリを割り当てる際、Rustは可能な限り明示的であろうとします。なので、もしあなたの関数がヒープ上のトレイトへのポインタを返す場合、例えばBox<dyn Animal>のように、リターン型にdynキーワードをつける必要があります。
struct Sheep {}
struct Cow {}
trait Animal {
// インスタンスメソッドのシグネチャ
fn noise(&self) -> &'static str;
}
// `Animal`というトレイトを`Sheep`に実装します。
impl Animal for Sheep {
fn noise(&self) -> &'static str {
"baaaaah!"
}
}
// `Cow`に`Animal`トレイトを実装します。
impl Animal for Cow {
fn noise(&self) -> &'static str {
"moooooo!"
}
}
// 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());
}