In Rust it is common to pass a struct of some sort that implements a certain trait, in which a trait-specific method is called on it. For example, using the strategy design pattern in the context of providing APIs for printers, there may be:

enum Error { /* ...truncated... */ }
enum Status { /* ...truncated... */ }
struct Order { /* ...truncated... */ }
struct PrintObject { /* ...truncated... */ }
 
trait PrinterControl {
    fn print(pages: &PrintObject) -> Result<Order, Error>;
    fn cancel(order: &Order) -> Result<(), Error>;
    fn check_health() -> Result<Status, Error>;
}

Then, in our code we may implement this PrinterControl trait like so:

struct Model1000 {}
impl PrinterControl for Model1000 { /* ...truncated... */ }
/* ...all the way to... */
struct Model5000 {}
impl PrinterControl for Model5000 { /* ...truncated... */ }

If we wanted to have our code accept a Printer, or more specifically any struct that implements the PrinterControl trait, we could do like so:

fn print_current_page(
    page_number: u32, selected_printer: &Box<dyn PrinterControl>
) -> Result<Order, Error> {
    selected_printer.print(document.by_number(page_number).into())
}

This approach makes use of dyn to enable using dynamic objects. Rust doesn’t know the size of the printer structs, because it could be any struct, so the size is unknown. This also means that we are unable to store it on the stack, so we use the smart pointer Box to dynamically allocate it onto the heap.

However, there is one issue with this approach, and that is while the code is short and concise, using Box and dyn is several times slower since we’re using the heap and the compiler can’t optimizer for that.

Static Dispatch vs. Dynamic Dispatch Benchmarks

With that being said, if it is known all the different possible values for a dynamic dispatched argument, Static Dispatch may be used instead.