Traits
Rust lets you abstract over types with traits. They’re similar to interfaces:
1. trait Greet {
2. fn say_hello(&self);
3. }
5. struct Dog {
6. name: String,
7. }
9. struct Cat; // No name, cats won't respond to it anyway.
11. impl Greet for Dog {
12. fn say_hello(&self) {
13. println!("Wuf, my name is {}!", self.name);
14. }
15. }
17. impl Greet for Cat {
18. fn say_hello(&self) {
19. println!("Miau!");
20. }
21. }
23. fn main() {
24. let pets: Vec<Box<dyn Greet>> = vec![
25. Box::new(Dog { name: String::from("Fido") }),
26. Box::new(Cat),
27. ];
28. for pet in pets {
29. pet.say_hello();
30. }
31. }
- Traits may specify pre-implemented (default) methods and methods that users are required to implement themselves. Methods with default implementations can rely on required methods.
- Types that implement a given trait may be of different sizes. This makes it impossible to have things like
Vec<Greet>in the example above. dyn Greetis a way to tell the compiler about a dynamically sized type that implementsGreet.- In the example,
petsholds Fat Pointers to objects that implementGreet. The Fat Pointer consists of two components, a pointer to the actual object and a pointer to the virtual method table for theGreetimplementation of that particular object.
Compare these outputs in the above example:
println!("{} {}", std::mem::size_of::<Dog>(), std::mem::size_of::<Cat>()); println!("{} {}", std::mem::size_of::<&Dog>(), std::mem::size_of::<&Cat>()); println!("{}", std::mem::size_of::<&dyn Greet>()); println!("{}", std::mem::size_of::<Box<dyn Greet>>());
