// silences a clippy warning so that my (weird) comments do not emit a warning #![allow(clippy::four_forward_slashes)] // OK, let's talk about traits // a trait is a collection of method that a type implements // here, any type that implements the `Printable` trait must implement the method `print`. trait Printable { fn print(&self); // this method could take other arguments as well // other methods could be placed here } // "impl `TraitName` for `TypeName`" lets you implement a trait for a type. // you must implement every method of the trait, otherwise it is a compiler error. impl Printable for u32 { fn print(&self) { println!("{}", self); } } impl Printable for &str { fn print(&self) { println!("{}", self); } } // Now we can write a function that takes any type implementing `Printable`. fn generic_print(x: &T) { x.print(); } // Read: "print takes any variable x of type T such that T implements Printable" // There are two ways to do this kind of generic function: // 1) static dispatch (as done above): // With static dispatch, the compiler looks at the actual type you call the function with. Then, the compiler writes a specialized function for this specific type. // Your program is usually faster to execute, but your executable is larger and the compilation time increases. // 2) dynamic dispatch // The compiler does not look at the actual type. Instead, it provides a single, generic implementation. // Your program is faster to compile (the compiler does less work) and your binary executable is smaller. // However, your program is slower to execute. // // Usually, static dispatch is the way to go (programs are compiled once, but executed many times, so it makes sense to optimse the execution speed). // --- static dispatch: //// fn generic_print(x: &T) { //// x.print(); //// } // You can also write: //// fn generic_print(x: &impl Printable) { //// x.print(); //// } // You can also write: //// fn generic_print(x: &T) //// where //// T: Printable, //// { //// x.print(); //// } // --- dynamic dispatch // note the `dyn` in the signature //// fn generic_print(x: &dyn Printable) { //// x.print(); //// } fn main() { // The function `generic_print`` can be called with two different types ! generic_print(&23); generic_print(&"Hello"); let mut v: Vec> = Vec::new(); v.push(Box::new(23)); v.push(Box::new("Hello")); for x in v { x.print(); } }