What does Rust error "you could box the found value and coerce it to the trait object" mean?

What does this error mean?

Reproduction: Rust Playground

error[E0308]: mismatched types
  --> src/main.rs:50:35
   |
50 |           PaymentType::InvoiceIn => InvoiceIn {
   |  ___________________________________^
51 | |             name: "Invoice 1".to_string(),
52 | |             payed: false,
53 | |         },
   | |_________^ expected `dyn Payable`, found `InvoiceIn`
   |
   = note: expected trait object `dyn Payable`
                    found struct `InvoiceIn`
   = help: `InvoiceIn` implements `Payable` so you could box the found value and coerce it to the trait object `Box<dyn Payable>`, you will have to change the expected type as well

For more information about this error, try `rustc --explain E0308`.

Code:

trait Payable {
    fn toggle_payed(&mut self);
}

enum PaymentType {
    InvoiceOut,
    InvoiceIn,
}

struct Payment {
    r#type: PaymentType,
}

struct InvoiceOut {
    name: String,
    payed: bool,
}

impl Payable for InvoiceOut {
    fn toggle_payed(&mut self) {
        self.payed = !self.payed
    }
}

struct InvoiceIn {
    name: String,
    payed: bool,
}

impl Payable for InvoiceIn {
    fn toggle_payed(&mut self) {
        self.payed = !self.payed
    }
}

fn save_invoice_in(invoice: &InvoiceIn) {
    println!("{}", invoice.name)
}

fn save_invoice_out(invoice: &InvoiceOut) {
    println!("{}", invoice.name)
}

fn main() {
    let payment = Payment {
        r#type: PaymentType::InvoiceOut, // This comes from user!
    };

    let payable: dyn Payable = match payment.r#type {
        PaymentType::InvoiceIn => InvoiceIn {
            name: "Invoice 1".to_string(),
            payed: false,
        },
        PaymentType::InvoiceOut => InvoiceOut {
            name: "Invoice 2".to_string(),
            payed: false,
        },
    };

    // Do something else with payable here

    payable.toggle_payed();

    // Do something else with payable here

    match payment.r#type {
        PaymentType::InvoiceIn => save_invoice_in(&payable),
        PaymentType::InvoiceOut => save_invoice_out(&payable),
    };
}

The dyn Payable type is unsized, which means that it can only exist behind a pointer. You cannot use it as the type of a local variable.

The Box type is a smart pointer, so you can do this:

let payable: Box<dyn Payable> = match payment.r#type {
    PaymentType::InvoiceIn => Box::new(InvoiceIn {
        name: "Invoice 1".to_string(),
        payed: false,
    }),
    PaymentType::InvoiceOut => Box::new(InvoiceOut {
        name: "Invoice 2".to_string(),
        payed: false,
    }),
};

This is the solution that is recommended by the error message.

4 Likes

What does Rust error “you could box the found value and coerce it to the trait object” mean?

Note that “you could box…” is not the error message. It's a help message. The error message is “error[E0308]: mismatched types … expected dyn Payable, found InvoiceIn”.

In order to understand what the “help:” and “note:” messages mean, you need to read the actual error message first. Many people tend to skip over the first line, but it's important to start there.

5 Likes