liveBook · Manning

Trait

As explained in Idiomatic Rust

A Rust language feature that defines shared behavior through required or provided methods without requiring a common inheritance hierarchy.

A trait defines behavior that different Rust types can share without sharing a state layout or an inheritance hierarchy. The trait names the behavior; each type’s implementation supplies it. Use traits when code needs a capability rather than a particular concrete type. section 2.2 section 2.2.1

A definition can be as small as `trait MinimalTrait {}`. More commonly, it declares methods that implementations must supply; methods may also have defaults. An `impl Trait for Type` attaches the behavior to a type. Traits can specify generic placeholders or require other traits through bounds. section 2.2.2

Listing 2.4 Implementing the Rectangular trait
pub trait Rectangular {        #1
    fn get_width(&self) -> i32;
    fn get_height(&self) -> i32;
    fn get_area(&self) -> i32;
}


impl Rectangular for Rectangle {     #2
    fn get_width(&self) -> i32 {
        self.width
    }
    fn get_height(&self) -> i32 {
        self.height
    }
    fn get_area(&self) -> i32 {
        self.width * self.height
    }
}

impl Rectangular for Square {       #3
    fn get_width(&self) -> i32 {
        self.length
    }
    fn get_height(&self) -> i32 {
        self.length
    }
    fn get_area(&self) -> i32 {
        self.length * self.length
    }
}

Compare the `Rectangular` implementations for `Rectangle` and `Square`: both expose width, height, and area while storing dimensions differently. The repeated implementations separate shared behavior from each type’s state. section 2.2.3

Require a capability in generic code

A generic function cannot call `t.describe()` merely because its parameter is named `T`. Define `SelfDescribing` with `fn describe(&self) -> String`, implement it for each accepted type, and constrain the function with `T: SelfDescribing`. The bound can appear inline or in a `where` clause. Without it, the compiler reports that the method is unavailable; without an implementation for a caller’s type, it reports an unsatisfied bound. If description needs no instance, the method can omit `&self` and be called as `T::describe()`. section 2.2.4

error[E0599]: no method named `describe` found for reference `&T` in
the current scope
 --> src/main.rs:6:7
  |
6 |     t.describe()
  |       ^^^^^^^^ method not found in `&T`
  |
  = help: items from traits can only be used if the type parameter is
  bounded by the trait
help: the following trait defines an item `describe`, perhaps you need to
restrict type parameter `T` with it:
  |
5 | fn describe_type<T: SelfDescribing>(t: &T) -> String {
  |                  ~~~~~~~~~~~~~~~~~

For more information about this error, try `rustc --explain E0599`.

The compiler error pinpoints the missing bound on `T` and suggests `T: SelfDescribing`. It is a useful check when a method call works on a concrete type but not in a generic function. section 2.2.4

For common traits, `#[derive(Clone, Debug, Default)]` can generate implementations instead of handwritten boilerplate. Write an implementation when its behavior must differ: a manual `Default` can supply chosen field values rather than the derived defaults. section 2.2.5

Traits are not a substitute for argument overloading. Two traits can give one type identically named methods, but an unqualified call may be ambiguous even when the signatures differ; bounds or explicit trait qualification clarify which method is meant. Use the flexibility deliberately: excessive traits can create pollution or duplication, and implementations involving external types face restrictions intended to prevent conflicts. section 5.2.4 section 7.2

How Idiomatic Rust covers it

How deep it goes
Works through definitions, implementations, bounds, and compiler errors; defers blanket implementations and advanced trait uses.
Examples
Tests shared geometry behavior for rectangles and squares, then diagnoses missing bounds in a generic description function.
What it assumes
Assumes familiarity with Rust syntax, borrowing, and lifetimes; develops trait definitions and bounds through examples.
What you can do after
Build it from scratch by defining a trait, implementing it for types, and bounding generic functions that call its methods.
How it connects
Links traits to generics, trait bounds, method-name ambiguity, and emulated optional arguments.

Also explained in

Compare how 4 books explain trait →

Related concepts

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