Traits

Trait Syntax

Traits are a way to define shared behavior that types can implement. They are similar to interfaces or abstract classes in other languages.

A trait consists of associated items: functions (including methods), types, and constants. Trait items are in scope only when their trait is.

The following example demonstrates a trait with one method and its implementation on a type (here, a struct). Trait functions and methods typically omit the function body by replacing it with a semicolon. The trait implementation then must provide the function body:

//! Trait example.

/// A trait with one method.
pub trait Summary {
    /// Returns a string that summarizes the item.
    /// Note that it can be just a method signature (no function body).
    fn summarize(&self) -> String;
}

pub struct NewsArticle {
    pub headline: String,
    pub location: String,
    pub author: String,
    pub content: String,
}

// Implement the `Summary` trait on the `NewsArticle` 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(),
    };
    // Since the type implements the trait, we can call its method.
    println!("Summary: {}", na.summarize());

    // The method call above is equivalent to:
    NewsArticle::summarize(&na);
    // Or, if we needed to fully disambiguate:
    <NewsArticle as Summary>::summarize(&na);
}

A trait can be implemented by a struct, an enum, but also by a union, a primitive type, a sequence (slice or array), a tuple, a function pointer or a reference / pointer type:


// An example trait (with a method with a default implementation):
trait Tr {
    fn foo(&self) {
        println!("foo");
    }
}

enum MyEnum {
    A,
    B,
}

// Implement a trait for an enum:
impl Tr for MyEnum {}

#[repr(C)]
union MyUnion {
    f1: u32,
    f2: f32,
}

// Implement a trait for a union:
impl Tr for MyUnion {}

// Implement a trait for a primitive type:
impl Tr for u32 {}

// Implement a trait for a slice:
impl Tr for [u32] {}

// Implement a trait for an array:
impl Tr for [u32; 3] {}

// Implement a trait for a tuple:
impl Tr for (u32, f64) {}

// Implement a trait for a function pointer:
impl Tr for fn() {}

// Implement a trait for a reference type:
impl Tr for &u32 {}

fn main() {
    let e = MyEnum::A;
    e.foo();
    let u = MyUnion { f1: 42 };
    u.foo();
    let s: &[u32] = &[1, 2, 3, 4];
    s.foo();
    let a: [u32; 3] = [1, 2, 3];
    a.foo();
    let t = (1, 2.0);
    t.foo();
    fn bar() {
        println!("bar");
    }
    // Function pointers can be created via a coercion from both function items
    // and non-capturing, non-async closures.
    let f: fn() = bar;
    f.foo();
    f();
    let r = &42;
    r.foo();
}

Provide a Default Implementation for a Trait's Function or Method

Traits can provide default implementations for their functions or methods. This allows types that implement the trait to either use the default implementation or override it with their own custom implementation:

/// A trait.
trait Summary {
    /// Method signature.
    /// Types that implement the `Summary` trait must implement a method with
    /// the exact signature.
    fn summarize_author(&self) -> String;

    /// Traits can also provide _default implementations_ for some or all of
    /// their methods or functions.
    /// - Default implementations have a body.
    /// - Types that implement the trait can override the default
    ///   implementations, if desired.
    fn summarize(&self) -> String {
        // Default implementations can call other methods in the same trait,
        // even if these other methods don't have a default implementation.
        // Here, the default implementation uses the `summarize_author` method.
        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 method 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>

Define Associated Types in Traits

Traits can have associated types, which are types that can be used in its functions and methods:

/// A trait that represents an iterator (similar to what is in the standard
/// library).
trait Iterator {
    /// This associated type is the type of the elements yielded by the
    /// iterator. You can't provide a default implementation at this point.
    type Item;

    /// A method to return the next element in the iterator, or `None` if the
    /// iterator is exhausted. Note the use of `Self::` 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.
    // You can use `Self` in the associated type definition.
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
        // This example iterator returns the same value forever.
        // `&mut self` is a shortcut for `self: &mut Self`,
        // where `Self` is the implementing type e.g. `MyIterator`.
        Some(self.0)
    }
}

/// Use the trait. Note how the associated type is specified within < and >.
fn use_iterator(it: &mut impl Iterator<Item = u32>) -> Option<u32> {
    it.next()
}

fn main() {
    let mut it = MyIterator(42);
    println!("{:?}", use_iterator(&mut it));
}

An associated type declaration can include generic parameters and trait bounds (see Generics for more details):

use std::fmt::Debug;
use std::fmt::Display;

trait Tr {
    type Item;
    // Associated type with a trait bound:
    type Item2: Display;
    // Associated type with multiple trait bounds:
    type Item3: Debug + Display;
    // Associated type with a generic type parameter:
    type Item4<T>;
    // Associated type with a lifetime, a generic type parameter, and a const generic:
    type Item5<'a, T, const N: usize>;
    // Associated type with a `where` bound:
    type Item6<T>
    where
        T: Clone;
}

A common pattern is a generic trait, with a generic type parameter with a default, plus an associated type for the output. The use of an associated type eliminates the need to write generic type parameters in many places:

/// A trait that represents the ability to add two values together.
/// Similar to `core::ops::Add`.
trait Add<Rhs = Self> {
    /// The type of the result of the addition.
    type Output;

    /// Adds two values together.
    fn add(self, rhs: Rhs) -> Self::Output;
}

Define Associated Constants in Traits

Traits can also define constants that implementing types can use. Associated constants may omit the equals sign and expression to indicate implementations must define the constant value:

/// A trait demonstrating the use of associated constants.
trait Example {
    /// An associated constant without a default value.
    ///
    /// Implementors of this trait must provide a value for this constant.
    const CONST_NO_DEFAULT: i32;

    /// An associated constant with a default value.
    ///
    /// Implementors of this trait can optionally override this default.
    const CONST_WITH_DEFAULT: i32 = 99;
}

/// A struct that implements the `Example` trait.
struct S;

impl Example for S {
    const CONST_NO_DEFAULT: i32 = 0;
}

fn main() {
    println!("{} {}", S::CONST_NO_DEFAULT, S::CONST_WITH_DEFAULT);
}

Require that a Type Implement Other Traits (with Supertraits)

A trait can require that implementing types also implement other traits. These are called supertraits:

use std::fmt;

// A supertrait is a trait that another trait depends on.
// In this case, `OutlinePrint` depends on `Display`.
// It means that any type that implements `OutlinePrint` must also implement
// `Display`.
//
// BEWARE: supertraits are NOT inheritance!
// They are only a constraint mechanism. When you define a trait with a
// supertrait, you're essentially saying, "Any type that implements this trait
// must also implement these other traits."  In inheritance, a subclass can
// inherit data (fields) and default implementations from its superclass. This
// is not true in Rust.

trait OutlinePrint: fmt::Display {
    fn outline_print(&self) {
        println!("* {self} *");
        // We can use `println!` here,
        // because `self` is guaranteed to implement `Display`.
    }
}

/// Implement the `OutlinePrint` trait for `String`.
/// `String` already implements the `Display` supertrait. That would not work
/// otherwise.
impl OutlinePrint for String {}

fn main() {
    String::from("test").outline_print();
}

Implement a Local Trait for a Type Defined Elsewhere

You can implement a trait defined in your crate on types defined outside of it - simply by writing a impl block. This is often used to extend the functionality of an external (e.g., standard library) type:

/// A trait for types that can be hashed to a `u64`.
trait MyHash {
    /// Returns a hash of the object.
    fn myhash(&self) -> u64;
}

/// Implement the `MyHash` trait for preexisting type `i64`,
/// which is defined in the standard library.
impl MyHash for i64 {
    fn myhash(&self) -> u64 {
        *self as u64
    }
}

fn main() {
    let x = 1i64;
    // Since `i64` implements the `MyHash` trait, we can call its method.
    println!("{}", x.myhash());
}

Implement a Trait on a Type, Both Defined Elsewhere, with the "Newtype" Pattern

One restriction to note is that you can implement a trait on a type only if at least one of the trait or the type is local to your crate. If neither are, use the "newtype" pattern.

The newtype pattern involves creating a new local type (typically, a tuple struct with a single field) to wrap an existing type. This allows you to implement traits on the wrapper type, even if you don't own the original type or the trait.

use std::fmt;

// // ERROR: only traits defined in the current crate can be implemented for
// // types defined outside of the crate:
// impl fmt::Display for Vec<String> {
//     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//         write!(f, "[{}]", self.join(", "))
//     }
// }

/// Tuple struct wrapping the type we want to add a non-local trait to.
///
/// This is the 'newtype pattern'.
struct Wrapper(Vec<String>);

/// Implement a (non-local) trait on the (local) `Wrapper` type:
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.

fn main() {
    println!(
        "{}",
        Wrapper(vec!["example".to_string(), "example 2".to_string()])
    );
}

Prohibit Trait Implementation with the Sealed Trait Pattern

The sealed trait pattern is a way to prevent crates from implementing a trait that they don't own. This allows the author of the trait to add new items to it in the future without that being a breaking change for downstream users.

This is typically achieved by defining a public trait that has a dependency on another "sealer" trait which is kept private to the crate. Because the sealer trait is not accessible outside of the crate, no downstream crate can satisfy the necessary trait bounds to implement the public-facing trait. More recently, the #[sealed] attribute has been introduced as a more direct and cleaner way to apply this pattern:

Provide a Blanket Implementation of a Trait

A "blanket implementation" implements a trait for any type that satisfies a given set of trait bounds. This allows for broad, generic functionality to be applied across a wide range of different types automatically, promoting code reuse and extensibility. A well-known example in Rust's standard library is the implementation of the ToString trait for any type that implements the Display trait, allowing any displayable type to be converted into a string.

Beware that blanket impl apply globally and can lead to conflicts if overused.

mod my {
    use std::fmt::Display;

    pub trait VeryUsefulTrait {
        fn my_method(&self) -> String;
    }

    // Blanket implementation for ALL types that implement `Display`:
    impl<T: Display> VeryUsefulTrait for T {
        fn my_method(&self) -> String {
            format!("{self}")
        }
    }
}

fn main() {
    // Don't forget to bring the trait in scope:
    use my::VeryUsefulTrait;

    let s = String::from("Hello, world!");

    // There is no need for an individual implementation
    // of `VeryUsefulTrait` for `String`, since it implements `Display`:
    println!("{}", s.my_method());
}

Define Generic Traits that Work with Multiple Types

Generic traits allow you to define a shared set of behaviors that can be implemented by multiple types, enabling code reuse and polymorphism:

/// A generic trait that can be implemented for different types.
/// `T` between angle brackets is a generic type parameter:
trait Test<T> {
    /// A function that takes a value of generic type `T` as its input.
    fn test(_t: T);
}

struct SomeStruct;

/// Generic implementation of a generic trait for a `struct`.
// Note that the `<>` brackets appear in two places.
impl<T> Test<T> for SomeStruct {
    fn test(_t: T) {
        println!("This is the test function.");
    }
}

fn main() {
    // `T` is `i32`:
    SomeStruct::test(1);
    // `T` is `bool`:
    SomeStruct::test(true);
}

Use a Trait Bound to Guarantee that a Generic Type Implements a Trait

Trait bounds are often used with generics to specify that a generic type must implement a particular trait. Traits bounds can be applied to generic parameters of a function, allowing the function to accept any type that implements the specified trait, or used in a type definition or impl block:

use std::collections::hash_map::DefaultHasher;
use std::hash::Hash;
use std::hash::Hasher;

/// `GenericTypeParameter: Trait` is called the "trait bound" syntax.
/// Trait bounds allow you to specify that a generic type must implement a
/// certain trait.
///
/// Here, 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);
    // The `finish` method returns the computed hash.
    println!("The hash is {:x}", hasher.finish());
}

/// This struct represents a pair of values.
///
/// It is generic over the types of the two values.
struct Pair<A, B> {
    first: A,
    second: B,
}

// Generics and trait bounds make it possible to implement a trait
// conditionally. Here, the `Pair` type implements `Hash` 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);
}

Trait bounds are specified using the TypeParameter: Trait syntax, where TypeParameter is a generic type parameter and Trait is the trait that the type must implement. There are written within < > or in a separate where clause, which can be more readable. The impl Trait syntax may also used:

trait Summary {
    fn summarize(&self) -> String;
}

/// This function accepts a reference to any type `T` that implements the
/// `Summary` trait.
fn notify<T: Summary>(item: &T) {
    // Since `item` is guaranteed to implement `Summary`, we can call its
    // `summarize` method.
    println!("Breaking news! {}", item.summarize());
}

/// It  may be equivalently be written in a `where` clause:
fn notify2<T>(_item: &T)
where
    T: Summary,
{
}

// The "impl Trait" syntax is in this case equivalent.
// `impl Summary` is an opaque type that implements the `Summary` trait:
fn notify3(_item: &impl Summary) {}

/// A struct that implements the `Summary` trait.
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);
    notify3(&a);
}

Bounds with multiple traits are possible:

use std::clone::Clone;
use std::fmt::Debug;

/// This function takes a reference to a type that must implement both the
/// `Debug` and `Clone` traits. Note the `+`
fn func<T: Debug + Clone>(t: &T) {
    println!("{:?}", t.clone());
    // We can call `clone` because `T` implements `Clone`.
    // We can also print `t` with `:?` because it implements `Debug`.
}

/// It  may be equivalently be written in a `where` clause:
fn func2<T>(t: &T)
where
    T: Debug + Clone,
{
    println!("{:?}", t.clone());
}

/// You can also use the similar `impl Trait` syntax.
/// `impl Trait` is an opaque type that implements the specified traits.
/// Parentheses are necessary.
fn func3(item: &(impl Debug + Clone)) {
    println!("{:?}", item.clone());
}

#[derive(Debug, Clone)]
struct S;

fn main() {
    let s = S;
    // Sinc `S` implements both `Debug` and `Clone`, we can call the above
    // functions:
    func(&s);
    func2(&s);
    func3(&s);
}

For more details on trait bounds, see the Generics and Impl Trait chapters.

Use Async with Traits

This topic is covered in the Async↗ chapter.

  • Enums.
  • Generics.
  • impl Trait.
  • Structs.
  • Trait Objects.
  • Rust Patterns.

References