CodeBlanks Logo Icon CodeBlanks

&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))
}

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

main.rs
use std::io::{self, Write};

fn main() {
    // buraya yaz
}

Sample solution

main.rs
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>&amp;mut dyn Write</code> needed instead of <code>&amp;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

Question
fn yaz(w: &____ dyn Write, mesaj: &str) -> std::io::Result<()> {
    writeln!(w, "{mesaj}")
}
Answer
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

All lessons in Advanced