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.