&dyn Trait
- Rust course
- Advanced
- Lesson 58/81
- 4 min
Do reference-based dispatch with &dyn Trait.
&dyn Trait
The form of a trait object that needs no ownership is the borrowed &dyn Trait and &mut dyn Trait. There's no heap allocation; its advantage over generics is that a single copy of the function is compiled. The standard library uses this often for I/O:
use std::io::Write;
fn yaz(w: &mut dyn Write, mesaj: &str) -> std::io::Result<()> {
writeln!(w, "{mesaj}")
}
let mut tampon: Vec<u8> = Vec::new();
yaz(&mut tampon, "merhaba")?; // to memory
yaz(&mut std::io::stdout(), "merhaba")?; // to the screenThe same function works with a file, a socket or a memory buffer.
Trait objects with lifetimes
Trait objects have a lifetime too. Box<dyn Trait> is Box<dyn Trait + 'static> by default; to box a type holding borrowed data, the lifetime must be written explicitly:
struct Bakis<'a>(&'a str);
impl Ses for Bakis<'_> {
fn ses(&self) -> String { self.0.to_string() }
}
fn kutula<'a>(s: &'a str) -> Box<dyn Ses + 'a> {
Box::new(Bakis(s))
}Practice Target-independent writing
Write the yaz function above. Write merhaba into a Vec<u8> buffer; convert the buffer to text and print it as merhaba (8 bayt).
Starter code
use std::io::{self, Write};
fn main() {
// buraya yaz
}Sample solution
use std::io::{self, Write};
fn yaz(w: &mut dyn Write, mesaj: &str) -> io::Result<()> {
writeln!(w, "{mesaj}")
}
fn main() -> io::Result<()> {
let mut tampon: Vec<u8> = Vec::new();
yaz(&mut tampon, "merhaba")?;
let metin = String::from_utf8_lossy(&tampon);
println!("{} ({} bayt)", metin.trim(), tampon.len());
Ok(())
}Expected output
merhaba (8 bayt)
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 Why is <code>&mut dyn Write</code> needed instead of <code>&dyn Write</code>?
- It isn't needed
- It's faster
- & can't be used with dyn
- Write::write_all takes &mut self; writing changes the target Correct answer
Explanation: A trait object must also fit the methods' self form; &dyn can only call &self methods.
2 Mutable trait object
fn yaz(w: &____ dyn Write, mesaj: &str) -> std::io::Result<()> {
writeln!(w, "{mesaj}")
}
fn yaz(w: &mut dyn Write, mesaj: &str) -> std::io::Result<()> {
writeln!(w, "{mesaj}")
}
Explanation: Since Write methods take &mut self, &mut dyn Write is needed.
3 Form and property
| Term | Meaning |
|---|---|
&dyn T |
Borrowed, read-only |
&mut dyn T |
Borrowed, mutating |
Box<dyn T> |
Owned, 'static |
Box<dyn T + 'a> |
Can hold borrowed data |