Advanced Traits

Supertrait Relationships

A supertrait relationship—declared as trait Subtrait: Supertrait (which is syntactic sugar for trait Subtrait where Self: Supertrait)—states that any type T implementing Subtrait must also provide an implementation of Supertrait.

Notice That Supertraits Are Constraints on Self, Not Subtype Inheritance

Unlike class inheritance in object-oriented languages, a supertrait does not embed fields into Subtrait, and impl Subtrait for MyType must still be paired with a separate impl Supertrait for MyType block. Inside default methods of Subtrait (and inside any generic function bounded by T: Subtrait), all methods and associated types of Supertrait are automatically available without repeating + Supertrait in the where clause:

Trait Hierarchy:
        +---------------+     +---------------+
        |  Identifiable |     |   Timestamped |
        +-------+-------+     +-------+-------+
                \                     /
                 v                   v
        +-------------------------------------+
        |  AuditEvent: Identifiable + ...     |
        +-------------------------------------+

Implementing for `LoginEvent` Requires Three Separate `impl` Blocks:
  1. `impl Identifiable for LoginEvent { ... }`
  2. `impl Timestamped for LoginEvent { ... }`
  3. `impl AuditEvent for LoginEvent { ... }`
trait Identifiable {
    fn id(&self) -> u64;
}

trait Timestamped {
    fn unix_millis(&self) -> u64;
}

// Multiple supertraits combined with `+`:
trait AuditEvent: Identifiable + Timestamped {
    // Default method can call `self.id()` and `self.unix_millis()` directly:
    fn format_audit_line(&self) -> String {
        format!(
            "[ts={} id={}] {}",
            self.unix_millis(),
            self.id(),
            self.action()
        )
    }

    fn action(&self) -> &str;
}

struct LoginEvent {
    event_id: u64,
    timestamp: u64,
    username: String,
}

impl Identifiable for LoginEvent {
    fn id(&self) -> u64 {
        self.event_id
    }
}

impl Timestamped for LoginEvent {
    fn unix_millis(&self) -> u64 {
        self.timestamp
    }
}

impl AuditEvent for LoginEvent {
    fn action(&self) -> &str {
        &self.username
    }
}

// Generic function only needs `E: AuditEvent` to access all 3 traits' methods:
fn emit_event<E: AuditEvent>(event: &E) {
    println!("{}", event.format_audit_line());
}

fn main() {
    let login = LoginEvent {
        event_id: 101,
        timestamp: 1_761_340_000_000,
        username: String::from("user=prime login_ok"),
    };
    emit_event(&login);
}
[ts=1761340000000 id=101] user=prime login_ok

Disambiguating Overlapping Supertrait Method Names

If two supertraits SuperA and SuperB (or SuperA and Subtrait) define a method with the exact same name (fn label(&self) -> &str), calling item.label() produces an ambiguity error (E0034). Use Fully Qualified Syntax—SuperA::label(&item) or <T as SuperB>::label(&item)—to select the intended supertrait method:

trait NetworkNode {
    fn address(&self) -> &str;
}

trait StorageNode {
    fn address(&self) -> &str;
}

trait ClusterNode: NetworkNode + StorageNode {}

struct Server {
    ip: String,
    disk_path: String,
}

impl NetworkNode for Server {
    fn address(&self) -> &str {
        &self.ip
    }
}

impl StorageNode for Server {
    fn address(&self) -> &str {
        &self.disk_path
    }
}

impl ClusterNode for Server {}

fn inspect_node<N: ClusterNode>(node: &N) {
    println!("network address: {}", NetworkNode::address(node));
    println!("storage address: {}", StorageNode::address(node));
}

fn main() {
    let srv = Server {
        ip: String::from("10.0.0.12:9000"),
        disk_path: String::from("/mnt/nvme0"),
    };
    inspect_node(&srv);
}
network address: 10.0.0.12:9000
storage address: /mnt/nvme0

Automatic Supertrait Implementation via Blanket Impls

When a subtrait adds no new required state and can be derived automatically from a supertrait (such as trait Subtrait: Supertrait), pairing the supertrait bound with a blanket implementation impl<T: Supertrait> Subtrait for T {} automatically implements Subtrait for every type that implements Supertrait.