Step 6 of 6
Deriving the standard traits
Many standard traits have one obvious implementation: compare the fields one by one, copy every field, print every field. For those, #[derive(...)] writes the impl for you. You've used #[derive(Debug)] since the structs module; here's the whole family.
| Derive | What you get | Needs |
|---|---|---|
Debug |
{:?} and {:#?} |
every field is Debug |
Clone |
.clone(), a deep copy |
every field is Clone |
Copy |
values are copied instead of moved | Clone, and every field is Copy (no String, no Vec) |
PartialEq |
== and !=, field by field |
every field is PartialEq |
Eq |
a promise that x == x is always true |
PartialEq; not possible with f64 fields (NaN != NaN) |
Hash |
can be a HashMap key or in a HashSet |
usually derived together with Eq |
PartialOrd |
<, >, <=, >= |
PartialEq |
Ord |
.sort(), .max(), BTreeMap keys |
Eq and PartialOrd |
Default |
Type::default(): zeros, false, empty strings and vectors |
every field is Default |
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
enum Priority {
Low,
Medium,
High,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
struct Ticket {
priority: Priority,
id: u32,
}
#[derive(Debug, Default)]
struct Settings {
volume: u8,
muted: bool,
nickname: String,
}
fn main() {
let mut tickets = vec![
Ticket { priority: Priority::High, id: 7 },
Ticket { priority: Priority::Low, id: 3 },
Ticket { priority: Priority::High, id: 2 },
];
tickets.sort();
for t in &tickets {
println!("{:?} #{}", t.priority, t.id);
}
let p = Priority::Medium;
let q = p; // Copy: p is still usable
println!("{} {}", p == q, Priority::High > Priority::Low);
let s = Settings::default();
println!("volume {}, muted {}, nickname {:?}", s.volume, s.muted, s.nickname);
}
Low #3
High #2
High #7
true true
volume 0, muted false, nickname ""
How derived comparisons work:
- Structs compare field by field in the order the fields are written: first
priority, and only if those are equal,id. That's why the two High tickets end up sorted by id. Reordering the fields changes the sort order. - Enums compare by the order the variants are written:
Low < Medium < High. Put variants in a meaningful order and sorting comes for free.
Which ones should you derive?
Derive Debug on almost everything: error messages and dbg! need it, and tests need it for assert_eq!. For small value types (ids, coordinates, enums without data), Clone, Copy, PartialEq, Eq, Hash is a common set. Libraries derive every standard trait that makes sense for a type, because users of the library can't add them later (the orphan rule). Don't derive what doesn't make sense: Copy on a big struct hides expensive copies, and Default for a type with no sensible empty value invites bugs.
Common mistakes
Copywith aStringfield. AStringowns heap memory, so it can't be copied bit by bit:
#[derive(Clone, Copy)]
struct Player {
name: String,
hp: u32,
}
fn main() {}
error[E0204]: the trait `Copy` cannot be implemented for this type
EqorOrdwith anf64field:error[E0277]: the trait bound `f64: Eq` is not satisfied. Floats only havePartialEq/PartialOrd. Store cents as an integer, or compare floats by hand.- Deriving
CopywithoutClone, orOrdwithoutPartialOrdandEq. Each needs the traits listed in its "Needs" column. The error names the missing one. - Expecting
derive(Display). It doesn't exist; there's no single obvious way to show a type to users. Write it by hand (previous step).
Your task
Release versions need to be compared, sorted, copied, used in a HashSet and printed with {:?}. Add one #[derive(...)] line to Version so all the hidden tests compile and pass. The field order already gives the right sort order (major, then minor, then patch).
Challenge 2
Fill the blank so Config gets a default() with every field set to its type's empty value, and the struct update syntax fills in the rest. The program should print 3 false "".
Challenge 3
A ladder ranks players by rank first and then by name (alphabetically). Add the derives Rank and Player need, then write fn top_player(players: &[Player]) -> Option<Player> that returns a clone of the highest player, or None for an empty slice. Use the derived comparison (>) rather than comparing fields yourself. With the derived order, between two players of the same rank the name that sorts later counts as higher: a Gold cal beats a Gold bea.
Previous: Multiple bounds, where clauses and returning impl Trait