🧮

Rustでトレイト境界のような形で変数を束縛する【dyn】

に公開
2025/08/20

当然ですが、Rustは基本的にコンパイル時に変数に束縛する値の型を判明させる必要があります。しかし、場合によっては実行時に型を決定したいことがあります。

例code

例えばこんな感じで各llmを選んで使うプログラムがあったとします。

main.rs
trait Responder {
    fn respond(&self) -> Result<String, std::io::Error>;
}

struct OpenAI {
    model: String,
}

impl OpenAI {
    fn new(model: &str) -> Self {
        Self {
            model: model.to_string(),
        }
    }
}

impl Responder for OpenAI {
    fn respond(&self) -> Result<String, std::io::Error> {
        Ok(String::from("I'm a chatGPT."))
    }
}

struct Google {
    model: String,
}

impl Google {
    fn new(model: &str) -> Self {
        Self {
            model: model.to_string(),
        }
    }
}

impl Responder for Google {
    fn respond(&self) -> Result<String, std::io::Error> {
        Ok(String::from("I'm a gemini."))
    }
}

fn main() {
    let selected_model = "openai";
    let model = if selected_model == "openai" {
        OpenAI::new("chatgpt")
    } else {
        Google::new("gemini")
    };
}

しかしこのコードはエラーになります。ifでtrueとfalseの場合で違う型(OpenAIとGoogle)が返るためにコンパイルエラーが出ています。

cargo check
    Checking dyn_trait v0.1.0 (~/Develop/for_zenn/dyn_trait)
error[E0308]: `if` and `else` have incompatible types
  --> src/main.rs:46:9
   |
43 |       let model = if selected_model == "openai" {
   |  _________________-
44 | |         OpenAI::new("chatgpt")
   | |         ---------------------- expected because of this
45 | |     } else {
46 | |         Google::new("gemini")
   | |         ^^^^^^^^^^^^^^^^^^^^^ expected `OpenAI`, found `Google`
47 | |     };
   | |_____- `if` and `else` have incompatible types

For more information about this error, try `rustc --explain E0308`.
error: could not compile `dyn_trait` (bin "dyn_trait") due to 1 previous error

ここでdynを使って変数をResponder traitを実装した型と制限をつけて定義すると実行時に型を決定できます。
また、コンパイル時に値のサイズが不明なのでBoxで囲みます。使い方的には関数のジェネリック型Tに近いと思う。

main.rs main()
fn main() {
    let selected_model = "openai";
    let model: Box<dyn Responder> = if selected_model == "openai" {
        Box::new(OpenAI::new("chatgpt"))
    } else {
        Box::new(Google::new("gemini"))
    };
}

ちなみにこうすれば表示までできます。ifで"openai"に固定しているので普通にI'm a chatGPT.と表示されて終わりですが。

fn main() {
    let selected_model = "openai";
    let model: Box<dyn Responder> = if selected_model == "openai" {
        Box::new(OpenAI::new("chatgpt"))
    } else {
        Box::new(Google::new("gemini"))
    };
    println!("{}", model.respond().unwrap());
}

参考

https://doc.rust-jp.rs/rust-by-example-ja/trait/dyn.html

Discussion