iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🧮

Binding Variables with Trait Bounds in Rust Using dyn

に公開

Naturally, Rust generally requires the type of a value bound to a variable to be determined at compile time. However, in some cases, you may want to decide the type at runtime.

Example code

For example, let's say you have a program like this where you select and use various LLMs.

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")
    };
}

However, this code will result in an error. A compilation error occurs because the if and else branches return different types (OpenAI and 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

Here, you can determine the type at runtime by defining the variable with a constraint as a type that implements the Responder trait using dyn. Also, because the size of the value is unknown at compile time, we wrap it in a Box. In terms of usage, I think it's similar to a generic type T in a function.

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"))
    };
}

By the way, you can even display the output like this. Since it's fixed to "openai" in the if statement, it will simply output I'm a chatGPT. and finish.

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());
}

References

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

Discussion