It is possible to create a function that accepts objects that implement a specific trait.

Static Dispatch

fn generic_speak<T: Speak>(speaker: &T) {
    println!("{0}", speaker.speak());
}

fn main() {
    let person = Person {};
    let dog = Dog {};

    generic_speak(&person);
    generic_speak(&dog);
}

Static dispatch is used here, which means that Rust compiler will generate specialized versions of generic_speak function for both Dog and Person types. This generation of specialized versions of a polymorphic function (or any polymorphic entity) during compilation is called Monomorphization.

Dynamic Dispatch

fn generic_speak(speaker: &Speak) {
    println!("{0}", speaker.speak());
}

fn main() {
    let person = Person {};
    let dog = Dog {};

    generic_speak(&person as &Speak);
    generic_speak(&dog); // gets automatically coerced to &Speak
}

Here, only a single version of generic_speak exists in the compiled binary, and the speak() call is made using a vtable lookup at runtime. Thus, using dynamic dispatch results in faster compilation and smaller size of compiled binary, while being slightly slower at runtime.

Objects of type &Speak or Box<Speak> are called trait objects.