Traits
pub trait Summary { fn summarize(&self) -> String; } pub struct NewsArticle { pub headline: String, pub location: String, pub author: String, pub content: String, } // Implement Trait on a Type impl Summary for NewsArticle { fn summarize(&self) -> String { format!("{}, by {} ({})", self.headline, self.author, self.location) } } fn main() { let na = NewsArticle { headline: "headline".to_string(), location: "location".to_string(), author: "author".to_string(), content: "...".to_string(), }; println!("Summary: {}", na.summarize()); }
Trait methods are in scope only when their trait is.
Default Implementation
trait Summary { // Method signature that describe the behavior of the types that implement // this trait. Function signatures do not include a function body // (note the ;). fn summarize_author(&self) -> String; // Traits can provide default implementations // for some or all of their methods. fn summarize(&self) -> String { // The default implementation has a body. // Default implementations can call other methods in the same trait, // even if those other methods don't have a default implementation. format!("(Read more from {}...)", self.summarize_author()) } } // Let's define a `Blog` struct... struct Blog { author: String, } // ...that implements the `Summary` trait. impl Summary for Blog { // All non-default-implemented methods must be implemented for the struct. fn summarize_author(&self) -> String { self.author.clone() } } fn main() { // Instantiate a Blog struct... let blog = Blog { author: "ferris".into(), }; // ...and call the function implemented by default in the trait. println!("{}", blog.summarize()); // Because we've implemented `summarize_author` for `Blog`, the `Summary` // trait has given us the behavior of the `summarize` method without // requiring us to write any more code. } // Adapted from https://doc.rust-lang.org/book/ch10-02-traits.html
Supertraits
use std::fmt; // BEWARE: supertrait are NOT inheritance! trait OutlinePrint: fmt::Display { fn outline_print(&self) { println!("* {} *", self); // We can use `println!` here, // because `self` is guaranteed to implement `Display` } } // String implements Display. That would not work otherwise. impl OutlinePrint for String {} fn main() { String::from("test").outline_print(); }
"Newtype" Pattern
Unlike interfaces in languages like Java, C# or Scala, new traits can be implemented for existing types.
trait MyHash { fn myhash(&self) -> u64; } impl MyHash for i64 { fn myhash(&self) -> u64 { *self as u64 } } fn main() { let x = 1i64; println!("{}", x.myhash()); }
One restriction to note is that we can implement a trait on a type only if at least one of the trait or the type is local to our crate. If neither are, use the newtype pattern:
use std::fmt; // Tuple struct wrapping the type we want to add a non-local trait to. struct Wrapper(Vec<String>); impl fmt::Display for Wrapper { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "[{}]", self.0.join(", ")) } } // If you want the new type to have every method the inner type has, // implement the `Deref` trait instead. fn main() { println!( "{}", Wrapper(vec!["example".to_string(), "example 2".to_string()]) ); }
Traits as Parameters
// Accepts any type that implements the specified trait: fn notify(item: &impl Summary) { println!("Breaking news! {}", item.summarize()); } // Trait bound syntax (mostly equivalent): fn notify2<T: Summary>(item: &T) { println!("Breaking news! {}", item.summarize()); } trait Summary { fn summarize(&self) -> String; } struct Article { txt: String, } impl Summary for Article { fn summarize(&self) -> String { self.txt.clone() } } fn main() { let a = Article { txt: String::from("Some text"), }; notify(&a); notify2(&a); }
Multiple Traits
use std::clone::Clone; use std::fmt::Debug; // Note the `+` fn a_function(item: &(impl Debug + Clone)) { println!("{:?}", item.clone()); } fn some_function<T, U>(_t: &T, _u: &U) -> i32 where T: Debug + Clone, // Note the `+` U: Debug + Clone, { 42 } #[derive(Debug, Clone)] struct S; fn main() { let s = S; a_function(&s); }
Return-position impl
Trait
impl
fn returns_closure() -> impl Fn(i32) -> i32 { |x| x + 1 } fn main() { let f = returns_closure(); println!("{}", f(1)); }
Generic Traits
trait Test<T> { fn test(_t: T); } struct SomeStruct; // Note the <> in two places: impl<T> Test<T> for SomeStruct { fn test(_t: T) { println!("test"); } } fn main() { SomeStruct::test(1); SomeStruct::test(true); }
Associated Types
trait Iterator { type Item; // <-- associated type // Note the use of :: to refer to the associated type fn next(&mut self) -> Option<Self::Item>; } struct MyIterator(u32); // We implement the trait for a given struct impl Iterator for MyIterator { // ...and define what associated type should be used here type Item = u32; fn next(&mut self) -> Option<Self::Item> { Some(self.0) } } fn use_iterator(it: &mut impl Iterator<Item = u32>) -> Option<u32> { it.next() } // A common pattern is a generic type (with a default) and an associated type: trait Add<Rhs = Self> { type Output; // <-- associated type fn add(self, rhs: Rhs) -> Self::Output; } fn main() { let mut it = MyIterator(42); println!("{:?}", use_iterator(&mut it)); }
Trait Bounds
use std::collections::hash_map::DefaultHasher; use std::hash::Hash; use std::hash::Hasher; // Trait bounds: the `print_hash` function is generic over an unknown // type `T`, but requires that `T` implements the `Hash` trait. fn print_hash<T: Hash>(t: &T) { let mut hasher = DefaultHasher::new(); t.hash(&mut hasher); println!("The hash is {:x}", hasher.finish()); } struct Pair<A, B> { first: A, second: B, } // Generics make it possible to implement a trait conditionally. // Here, the Pair type implements Hash if, and only if, // its components do. impl<A: Hash, B: Hash> Hash for Pair<A, B> { fn hash<H: Hasher>(&self, state: &mut H) { self.first.hash(state); self.second.hash(state); } } fn main() { let p = Pair { first: 1, second: "2", }; print_hash(&p); }
Constants in Traits
trait Example { const CONST_NO_DEFAULT: i32; const CONST_WITH_DEFAULT: i32 = 99; } struct S; impl Example for S { const CONST_NO_DEFAULT: i32 = 0; } fn main() { println!("{} {}", S::CONST_NO_DEFAULT, S::CONST_WITH_DEFAULT); }
Async and Traits
See Async⮳.