Traits, Protocols, Interfaces
#![allow(unused)] fn main() { trait Receiver { fn send(&self, message: &str); } struct Email { email: String, } impl Receiver for Email { fn send(&self, message: &str) { println!("Email to {}: {}", self.email, message); } } struct ChatId { uuid: [u8; 16], } impl Receiver for ChatId { fn send(&self, message: &str) { println!("Chat message sent to {:?}: {}", self.uuid, message); } } }
-
Rust’s concept of polymorphism and generics is heavily built around traits.
-
Traits are requirements on a type in a generic context.
-
Requirements function much like a compile-time checked duck typing.
Duck typing is a concept from the practice of dynamic, untyped languages like Python, “if it walks like a duck and quacks like a duck, it’s a duck.”
That is, types with the methods and fields expected by a function are all valid inputs for that function. If a type implements methods, it is that type in a duck-typing context.
Traits behave like a static duck typing mechanism, in that we specify behavior rather than type. But we get the compile-time checks on if that behavior does really exist.
-
Alternatively: Traits are like collections of propositions, and implementing a trait for a type is a proof that the type can be used wherever the trait is asked for.
Traits have required methods, implementing those methods is the proof that a type has the required behavior.
reference:
- https://doc.rust-lang.org/reference/items/traits.html