Rust
&dyn Trait
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 screen
The 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))
}
Exercise 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).
Show example 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(())
}
Test yourself
-
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
Show answer
Write::write_all takes &mut self; writing changes the target — A trait object must also fit the methods' self form; &dyn can only call &self methods.
-
Mutable trait object
fn yaz(w: &____ dyn Write, mesaj: &str) -> std::io::Result<()> {
writeln!(w, "{mesaj}")
}
Show answer
fn yaz(w: &mut dyn Write, mesaj: &str) -> std::io::Result<()> {
writeln!(w, "{mesaj}")
}
-
Form and property
- &dyn T
- Borrowed, read-only
- &mut dyn T
- Borrowed, mutating
- Box<dyn T>
- Owned, 'static
- Box<dyn T + 'a>
- Can hold borrowed data
Next lesson: Summary - Trait objects