We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Generics and Traits step 8 of 24
Supertraits and default methods
A trait can require another trait. Here Greet requires Named:
pub trait Named {
fn name(&self) -> String;
}
pub trait Greet: Named {
fn greet(&self) -> String {
format!("hi {}", self.name())
}
}
Named is called a supertrait of Greet. The line pub trait Greet: Named
states a requirement: a type may implement Greet only if it also implements
Named. In exchange, code written inside Greet, such as the method body
above, may call Named's methods on self.
The colon resembles class inheritance, but the meaning is narrower. Greet
gains no fields or data from Named, and an impl Greet for T block cannot
define or replace Named's methods; name must be written in a separate
impl Named for T block. The standard library uses the same kind of
requirement in Copy: Clone, Eq: PartialEq and Ord: Eq + PartialOrd.
Default methods
A trait method that has a body, like greet above, is a default method.
Each implementing type may either use that body or supply its own method with
the same signature. A method without a body, like name, must be written by
every implementor.
Because greet is Greet's only method and it has a default, an empty block
is a complete implementation of Greet:
impl Greet for Person {}
This block adds a working greet method to Person, provided Person also
implements Named.
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 a pair (kind, name). greet_all returns one greeting per spec,
in the same order as the input. An empty input returns an empty vector.
- If
kindis exactly"robot", the spec builds aRobot. The comparison is case-sensitive and does no trimming, so"Robot"and" robot"are not robots. A robot'sname()returns the name converted with Rust'sstr::to_uppercase, andRobotreplaces the defaultgreetwith one that returns"BEEP "followed byname(). For example,("robot", "hal")produces"BEEP HAL". - Any other
kind, including the empty string, builds aPerson. A person'sname()returns the name unchanged, andPersonuses the defaultgreet. For example,("person", "ada")produces"hi ada".
Names may be empty or contain any Unicode text. An empty name still keeps the
space: "BEEP " or "hi ". to_uppercase follows Unicode case mapping, so
"héllo" becomes "HÉLLO", and the result can be longer than the input (for
example, "ß" becomes "SS").
The starter already builds each value as a Box<dyn Greet>, a pointer to a
value whose concrete type is chosen at run time, and calls greet through
it. You do not need to change greet_all.
Because every Greet type is also a Named type, Named's methods can be
called through a dyn Greet as well: who.name() compiles. Since Rust 1.86,
a &dyn Greet can also be converted to a &dyn Named.
The compiler error in the starter
The starter implements Greet for both types and Named for neither.
Compiling it produces errors like this one (abridged):
error[E0277]: the trait bound `Person: Named` is not satisfied
|
| impl Greet for Person {}
| ^^^^^^ unsatisfied trait bound
|
help: the trait `Named` is not implemented for `Person`
note: required by a bound in `Greet`
|
| pub trait Greet: Named {
| ^^^^^ required by this bound in `Greet`
The error points at the impl Greet line, but the missing piece is the
supertrait: the fix is a new impl Named for Person block, not a change to
the impl Greet block. The same E0277 appears for Robot. The compiler also
reports E0599 ("no method named name found") inside Robot's greet,
because name is not available on a type that does not implement Named.
All of these errors go away once both Named implementations exist.
A related error you may meet elsewhere is E0038, "the trait Greet is not
dyn compatible". A trait used as dyn Greet must be dyn compatible, and so
must its supertraits. If Named gained a generic method, or a method that
returns Self, without a where Self: Sized bound on that method, then
Box<dyn Greet> would stop compiling even though Greet itself did not
change.
Grading
Your code must compile, pass the tests and pass cargo clippy -- -D warnings
(Clippy with every warning treated as an error). That includes rustc's
dead_code lint, which warns about private items nothing uses. Keep the
pub visibility the starter gives the traits and types.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.