Traits

Implementing Traits

An implementation block (impl Trait for Type) binds a trait contract to a concrete or generic type by supplying definitions for every required item of the trait.

Basic impl Trait for Type Syntax

Unlike an inherent implementation (impl Type), a trait implementation names both the trait and the target type separated by the for keyword:

impl Summary for Article { ... }
     ^^^^^^^     ^^^^^^^
     Trait       Target Type (bound to `Self` inside the block)

Every method inside impl Trait for Type must match the parameter types, receiver mode (&self, &mut self, self), and return type declared in the trait definition:

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

struct Article {
    title: String,
    author: String,
}

struct Commit {
    hash: &'static str,
    message: &'static str,
}

impl Summary for Article {
    fn headline(&self) -> String {
        format!("{} (by {})", self.title, self.author)
    }
}

impl Summary for Commit {
    fn headline(&self) -> String {
        format!("[{}] {}", self.hash, self.message)
    }
}

fn main() {
    let article = Article {
        title: String::from("Zero-Cost Abstractions"),
        author: String::from("Ada"),
    };
    let commit = Commit {
        hash: "a1b2c3d",
        message: "fix parser bounds check",
    };

    println!("{}", article.headline());
    println!("{}", commit.headline());
}
Zero-Cost Abstractions (by Ada)
[a1b2c3d] fix parser bounds check

Completeness and Extra-Method Restrictions

The compiler enforces two strict structural checks on every impl Trait for Type block:

  1. All required items must be implemented (E0046): Omitting any required method, associated type, or associated constant prevents compilation.
  2. Only items belonging to the trait may appear (E0407): You cannot add extra helper methods inside impl Trait for Type that are not declared in Trait; place type-specific helpers in a separate inherent impl Type block instead.
// ❌ does not compile
trait Runner {
    fn run(&self);
    fn name(&self) -> &str;
}

struct Worker;

impl Runner for Worker {
    fn run(&self) {}

    fn helper(&self) {}
}

fn main() {}
error[E0407]: method `helper` is not a member of trait `Runner`
  --> src/main.rs:12:5
   |
12 |     fn helper(&self) {}
   |     ^^^^^^^^^^^^^^^^^^^ not a member of trait `Runner`

error[E0046]: not all trait items implemented, missing: `name`
  --> src/main.rs:9:1
   |
4  |     fn name(&self) -> &str;
   |     ----------------------- `name` from trait
...
9  | impl Runner for Worker {
   | ^^^^^^^^^^^^^^^^^^^^^^ missing `name` in implementation

Implementing Local Traits for Built-In and Standard Types

Because impl Trait for Type is decoupled from the type's original declaration, you can implement your own local trait on primitive types (i32, bool), slices ([T]), tuples, or standard library structs (String, Vec<T>):

trait ClampZero {
    fn clamp_non_negative(self) -> Self;
}

impl ClampZero for i32 {
    fn clamp_non_negative(self) -> Self {
        if self < 0 { 0 } else { self }
    }
}

impl ClampZero for f64 {
    fn clamp_non_negative(self) -> Self {
        if self < 0.0 { 0.0 } else { self }
    }
}

fn main() {
    println!("i32: {}", (-15_i32).clamp_non_negative());
    println!("f64: {}", (-3.5_f64).clamp_non_negative());
    println!("pos: {}", 42_i32.clamp_non_negative());
}
i32: 0
f64: 0
pos: 42

Implementing a Trait for a Unit Struct or Reference Type

Unit structs (struct SystemClock;) take zero bytes at runtime and are frequently used as stateless strategy types that implement a trait. Similarly, a trait can be implemented directly for a shared reference type &T or a slice &[T]:

trait ByteLength {
    fn byte_len(&self) -> usize;
}

impl ByteLength for &str {
    fn byte_len(&self) -> usize {
        self.len()
    }
}

impl<T> ByteLength for &[T] {
    fn byte_len(&self) -> usize {
        std::mem::size_of_val(*self)
    }
}

fn main() {
    let s: &str = "rust";
    let nums: &[u32] = &[10, 20, 30];

    println!("str bytes:   {}", s.byte_len());
    println!("slice bytes: {}", nums.byte_len());
}
str bytes:   4
slice bytes: 12