RustArena

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:

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

#[derive(Clone, Copy)]
struct Player {
    name: String,
    hp: u32,
}

fn main() {}
error[E0204]: the trait `Copy` cannot be implemented for this type

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