liveBook · Manning

Trait

As explained in Learn Rust in a Month of Lunches

A Rust trait is an abstract interface that defines capabilities or behavior a type can provide.

A trait describes what a type can do. Think of it as a qualification: different types can have the same qualification without becoming the same type. When a type implements a trait, it provides the trait’s methods, so code that requires that trait can rely on those methods being available. section 7.1 section 7.6

There are two common ways to give a type a trait:

  • Use an attribute such as `#[derive(Debug)]` when Rust can generate the implementation. section 7.1
  • Write `impl TraitName for TypeName` when the behavior needs your decisions. For example, Rust cannot guess what adding two values of your own struct should mean. section 7.1

A trait method can have a body, which supplies default behavior, or only a signature, which specifies its name, inputs, and return type. A type implementing a signature-only method must write its own body. Choose a default when most types should behave alike; require an implementation when their behavior will differ. section 7.1 section 7.1.1

use std::fmt;
 
struct Cat {
    name: String,
    age: u8,
}
 
impl fmt::Display for Cat {
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
      write!(f, "{} is a cat who is {} years old", self.name, self.age)
  }
}
 
fn main() {
    let mr_mantle = Cat {
        name: "Reggie Mantle".to_string(),
        age: 4,
    };
    println!("{mr_mantle}");
}

Here, `Cat` implements `Display` by writing its own `fmt` method. That lets `println!` print a `Cat` with `{}`. The unfamiliar formatter types can wait; the key step is supplying the behavior that `Display` requires. section 7.1.1

Compiler feedback can point you to a missing trait. If `{}` printing says a type does not implement `Display`, try `{:?}` for `Debug` printing if that type supports it. For an unfamiliar trait, check its documentation for “Required Methods”: those are the methods you will usually need to write. Traits also work as bounds on generic functions, letting a function accept different types that share the capabilities it needs. section 1.8 section 13.1.4 section 5.3

How Learn Rust in a Month of Lunches covers it

How deep it goes
Works through derived and manual implementations, default and required methods, and compiler feedback; leaves some Display formatter syntax unexplained.
Examples
Uses a derived Debug implementation, a custom Display implementation for a cat, and a compiler error from printing the unit type.
What it assumes
Assumes basic familiarity with Rust structs, methods, and printing, while introducing traits as capabilities before developing their syntax.
What you can do after
Build it from scratch by declaring a trait and implementing its methods for a type; use required-method documentation to guide the implementation.
How it connects
Connects traits to methods, structs, generics, trait bounds, and Display and Debug formatting.

Also explained in

Compare how 4 books explain trait →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Learn Rust in a Month of Lunches. The section links point to the parts of the book it draws on.