Skip to content

← Generics and Traits step 8 of 24

Medium Primitives

Supertraits and default methods

A trait can require another trait:

pub trait Greet: Named {
    fn greet(&self) -> String {
        format!("hi {}", self.name())
    }
}

Named is the supertrait. The colon looks like inheritance from a class-based language and it is not: there is no shared data, no overriding, and Greet is not a “kind of” Named. It is a bound — “to implement Greet you must already implement Named“ — and in exchange, code inside Greet (including the default body above) may call Named‘s methods on self.

This is exactly how the standard library layers its own traits: Copy: Clone, Eq: PartialEq, Ord: Eq + PartialOrd. Once you recognise the pattern, those signatures stop looking like magic.

Default methods

greet above has a body. That makes it a default: an implementor may accept it or replace it. And when every method of a trait has a default, this is a complete, legal implementation:

impl Greet for Person {}

An empty block that nevertheless adds behaviour. It surprises people the first time, and it is the mechanism behind the extension-trait pattern you will meet later in this track.

Your task

pub trait Named { fn name(&self) -> String; }
pub trait Greet: Named { fn greet(&self) -> String { ... } }

pub struct Person(String);
pub struct Robot(String);

pub fn greet_all(specs: Vec<(String, String)>) -> Vec<String>

Each spec is (kind, name).

  • "robot" builds a Robot. Its name() returns the name uppercased, and it overrides greet to produce "BEEP HAL".
  • anything else builds a Person. Its name() returns the name unchanged and it takes the default greet, producing "hi ada".

The dispatch happens through Box<dyn Greet>, so both types are used through the same trait object. Note that this compiles at all only because the supertrait bound rides along: a dyn Greet is usable as a Named too.

The error you must meet

The starter implements Greet for both types and Named for neither. Compile it:

error[E0277]: the trait bound `Person: Named` is not satisfied
   |
16 | impl Greet for Person {}
   |                ^^^^^^ the trait `Named` is not implemented for `Person`

Read that twice. You wrote impl Greet and the compiler is complaining about Named. That indirection is exactly what trips people up: the unmet requirement is the supertrait, and the fix is in a different impl block from the one you were editing. Expect it and it becomes obvious; meet it cold and you will stare at Greet looking for the mistake.

A second, rarer code in the neighbourhood: E0038, “the trait cannot be made into an object”. A supertrait bound propagates into dyn compatibility, so if Named had a generic method or returned Self, Box<dyn Greet> would stop compiling even though nothing about Greet changed.

One gate detail

Both traits stay pub. Under -D warnings rustc’s own dead_code lint is an error, and a trait or method nothing reaches is dead code. Keep the visibility the starter gives you.

Remember the grade is compile + tests + clippy -D warnings.