Rust Derivable Traits
Derivable traits are the standard traits the compiler can write an impl for on request. A #[derive(...)] line makes that request.
Eq is a promise floats cannot make
Eq is PartialEq plus the promise that every value equals itself. A float cannot promise it, so f64 has PartialEq but not Eq — and HashMap keys must be Eq:
println!("nan == nan is {}", f64::NAN == f64::NAN);Hash lets your type be a key
Derive Hash and your type can be a HashMap key or HashSet member. The derived hash mixes in every field:
#[derive(PartialEq, Eq, Hash)]
struct Point { x: i32, y: i32 }
let mut set = std::collections::HashSet::new();
set.insert(Point { x: 1, y: 2 });
println!("has point = {}", set.contains(&Point { x: 1, y: 2 }));Ord sorts by fields, in order
Ord is PartialOrd plus a guaranteed order for every pair. It needs Eq and PartialOrd first. BTreeSet values must have it. Structs compare field by field, in the order you wrote them: major before minor here. (The Debug derive is what prints each version.)
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Version { major: u32, minor: u32 }
let mut versions = std::collections::BTreeSet::new();
versions.insert(Version { major: 2, minor: 0 });
versions.insert(Version { major: 1, minor: 9 });
println!("smallest = {:?}", versions.first().unwrap());Earlier variants count as smaller
On enums, derived ordering follows the order the variants are declared in:
#[derive(PartialEq, PartialOrd)]
enum Size { Small, Medium, Large }
println!("small < large = {}", Size::Small < Size::Large);Copy refuses a String field
Copy copies stack bits only. No code runs. Every field must be Copy. The type must also be Clone. A String field rules Copy out:
#[derive(Clone, Copy)]
struct Label { text: String }
// error E0204: the trait `Copy` cannot be implemented for this typeDefault fills the fields you skip
Default::default() builds the type from each field's default: 0, false, empty text. And ..Default::default() fills just the fields you did not set:
#[derive(Debug, Default)]
struct Alarm { hour: u8, loud: bool }
let a = Alarm { hour: 7, ..Default::default() };
println!("{:?}", a);Example
#[derive(Debug, Clone, PartialEq, Default)]
struct Settings { volume: u8, muted: bool }
fn main() {
let a = Settings::default();
let b = a.clone();
println!("{:?}", a);
println!("same = {}", a == b);
println!("volume = {}", a.volume);
}Key points
- PartialEq gives == and !=
- Values compare field by field
- On enums, a value equals only the same variant
- Display is NOT on the menu
- The compiler cannot guess what a user should see
- So you write Display by hand
- Derive eagerly on types you publish
- The orphan rule says a trait impl must live in one place
- That place is the crate that owns the type or the trait
- So a common trait you skip is one your users can never add for you
- The publishing starter set is Debug, Clone and PartialEq, almost always
- Add Eq, Hash, Ord and PartialOrd when ordering or key use makes sense
- Add Default when an obvious empty value exists
- Deriving means the compiler writes the trait impl from the type's fields
- The menu of derivable traits is fixed by the standard library.
In this example, one derive line buys Settings four traits. Default builds a from each field's default: volume 0 and muted false. Debug lets {:?} print the whole struct. Clone makes b a full copy of a. PartialEq then compares the copies field by field, so a == b prints same = true. The last line reads volume straight from a: 0, the number default. Four impls, zero written by hand.
Note
Deriving is mechanical, not magic
Each generated impl does exactly the field-by-field job you would write yourself
And it is refused when any field lacks the trait
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
A struct holds a String field. Which trait can it NOT derive?