Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

dyn によるトレイトの返却

Rust コンパイラは、すべての関数の戻り値の型がどれだけの領域を必要とするかを知る必要があります。つまり、すべての関数は具象型を返さなければなりません。他の言語とは異なり、Animal のようなトレイトがある場合、Animal を返す関数を書くことはできません。なぜなら、その異なる実装は異なる量のメモリを必要とするからです。

ただし、簡単な回避策があります。トレイトオブジェクトを直接返す代わりに、関数は何らかの Animal含む Box を返します。box はヒープ内のメモリへの参照にすぎません。参照は静的に既知のサイズを持ち、コンパイラはそれがヒープに割り当てられた Animal を指していることを保証できるため、関数からトレイトを返すことができます!

Rust は、ヒープ上にメモリを割り当てるときは常に、できるだけ明示的であろうとします。そのため、このように関数がヒープ上のトレイトへのポインタを返す場合、戻り値の型を dyn キーワードを使って記述する必要があります。例: Box<dyn Animal>

struct Sheep {}
struct Cow {}

trait Animal {
    // インスタンスメソッドのシグネチャ
    fn noise(&self) -> &'static str;
}

// `Sheep` に `Animal` トレイトを実装します。
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!("ランダムに動物を選択しました。その鳴き声は {} です", animal.noise());
}