Lesson 8.2 · 10 min
One name for anything that replies
Chatterbox has a fake reply today and will have a real model tomorrow. The chat loop should not care which. A trait is how Rust lets you say "anything that can reply", and swap the thing behind it without touching the loop.
Adds to ChatterboxA Replier trait with an echo and a pretend model.
A promise with a name
Right now, something has to produce the bot's reply. Today that is a function that echoes your words. Soon it will be a call to a real model over the internet. A chat loop that says "call the echo function" would have to be rewritten for the model. A chat loop that says "ask whatever replies" would not.
A trait is a list of things a type can do, with a name. It is a promise: any type that implements this trait has these methods. Ours is called Replier and promises one method, reply, which takes the user's text and gives back text.
$ cargo run
you said: hi
[big-model] pretend answer to: hiRead impl Replier for EchoReplier as "here is how EchoReplier keeps the promise". The two structs have nothing else in common: one holds nothing, the other holds a model name. Both can reply. For now ModelReplier only pretends. In the next lesson it does the real call, and nothing else in the program changes.
When a type breaks the promise
A function can say "I accept anything that implements Replier" by writing &dyn Replier in its argument. If you hand it something that never made the promise, the compiler stops you.
What happens when this program is compiled?
- It compiles and runs
- Compile error E0277
- It compiles, then panics
Reveal the answer
It does not compile: E0277. E0277 is "the trait bound is not satisfied". Mute never wrote impl Replier for Mute, so it cannot be used where a Replier is expected. This is checked before the program ever runs.
error[E0277]: the trait bound `Mute: Replier` is not satisfied --> src/main.rs:12:10 | 12 | talk(&Mute, "hello"); | ^^^^^ unsatisfied trait bound | help: the trait `Replier` is not implemented for `Mute` --> src/main.rs:5:1 | 5 | struct Mute; | ^^^^^^^^^^^ help: this trait has no implementations, consider adding one --> src/main.rs:1:1 | 1 | trait Replier { | ^^^^^^^^^^^^^ = note: required for the cast from `&Mute` to `&dyn Replier`
Many kinds behind one box
Sometimes you want to choose the implementor while the program runs: if there is an API key, use the model; if not, echo. The two structs have different sizes, so a variable cannot hold "either" directly. The answer is Box<dyn Replier>. A Box is a pointer to a value on the heap, and dyn means "some type that implements this trait, decided at run time". The box has one fixed size whatever is inside it.
That gives the chat loop one type to work with, and the code that builds the box is the only place that knows which kind it is. Next lesson, that choice is made from an environment variable.
Finish the two impl blocks (echo says you said: <msg>, the model says [<model>] pretend answer to: <msg>), then write all_replies so it asks every boxed replier in order.
Finish the two impl blocks (echo says you said: <msg>, the model says [<model>] pretend answer to: <msg>), then write all_replies so it asks every boxed replier in order.
Hidden tests (4): all_replies_keeps_order echo_repeats no_repliers_no_replies model_names_itself
This exercise runs real Rust in your browser through the official Rust Playground, so it needs JavaScript.
What you have now
Chatterbox v0.8 has a Replier trait, a fake EchoReplier, and a ModelReplier that pretends. The chat loop only has to hold a Box<dyn Replier>. Next lesson, ModelReplier stops pretending.
Say it out loud, then reveal
What is a trait?Reveal
impl Trait for Type keeps the promise.What does <code>Box<dyn Replier></code> mean?Reveal
What error appears when you pass a type that does not implement the trait?Reveal
That is lesson 8.2. Tick it off so the course remembers where you are.
Go deeper
Questions people ask
What is a trait in Rust?
A set of method signatures that different types can implement. It is Rust's way of describing shared behaviour, similar to an interface in other languages.
What is the difference between dyn Trait and impl Trait?
impl Trait in an argument fixes one concrete type at compile time. dyn Trait, usually behind a Box or a reference, lets the concrete type be chosen while the program runs.
Why do I need Box for a trait object?
Different implementors have different sizes and a variable needs one size. A Box is a pointer, which always has the same size, to the value on the heap.
Sources The Rust Book: Traits