Deriving Standard Traits
The #[derive(...)] attribute instructs the compiler to automatically generate standard trait implementations for a struct by delegating the operation field-by-field in declaration order. A trait can be derived on a struct only if every field of the struct also implements that trait.
Built-In Derivable Standard Library Traits
Trait in #[derive(...)] |
Generated Method(s) | What the Derived Implementation Does |
|---|---|---|
Debug |
fmt(&self, f: &mut Formatter) |
Formats the struct name and all fields for {:?} and {:#?} |
Clone |
clone(&self) -> Self |
Calls .clone() on every field to produce a deep duplicate |
Copy |
(marker trait; requires Clone) |
Marks the struct as bitwise-copyable on assignment (all fields must be Copy, and the struct cannot implement Drop) |
PartialEq |
eq(&self, other: &Self) -> bool |
Enables == and !=; returns true if every field equals the corresponding field in other |
Eq |
(marker trait; requires PartialEq) |
Asserts that == is reflexive (a == a), which holds for integers/strings but not f32/f64 (NaN != NaN) |
PartialOrd |
partial_cmp(&self, other: &Self) |
Enables <, <=, >, >= by comparing fields lexicographically from top to bottom |
Ord |
cmp(&self, other: &Self) -> Ordering |
Total lexicographical ordering (requires Eq and PartialOrd); needed for BTreeMap keys and .sort() |
Hash |
hash<H: Hasher>(&self, state: &mut H) |
Feeds every field into the hasher in order; needed (with Eq) for HashMap keys and HashSet elements |
Default |
default() -> Self |
Initializes every field with FieldType::default() |
Deriving Common Traits Together
For plain value structs that do not contain floating-point numbers, it is idiomatic to derive Debug, Clone, PartialEq, Eq, Hash, Default (and Copy if the struct is small and contains only Copy fields):
use std::collections::HashSet;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
struct Version {
major: u16,
minor: u16,
patch: u16,
}
fn main() {
// 1. `Default` creates `0.0.0`
let v0 = Version::default();
// 2. `Copy` + `Clone` allow copying without moving
let v1 = Version { major: 1, minor: 80, patch: 0 };
let v2 = Version { major: 1, minor: 85, patch: 1 };
// 3. `Ord` + `PartialOrd` compare fields top-to-bottom (`major`, then `minor`, then `patch`)
let mut releases = [v2, v0, v1];
releases.sort();
println!("Sorted releases ({:?}):", releases);
// 4. `Hash` + `Eq` allow storing `Version` in a `HashSet` or `HashMap`
let mut seen = HashSet::new();
seen.insert(v1);
println!("Set contains v1? {}", seen.contains(&v1));
}
Sorted releases ([Version { major: 0, minor: 0, patch: 0 }, Version { major: 1, minor: 80, patch: 0 }, Version { major: 1, minor: 85, patch: 1 }]):
Set contains v1? true
Field Declaration Order Affects Derived PartialOrd and Ord
Because derived PartialOrd and Ord compare fields from first to last as declared in the struct, always place the primary sort key first:
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Task {
priority: u8, // Compared 1st
id: u32, // Compared 2nd (tie-breaker)
}
fn main() {
let t1 = Task { priority: 1, id: 999 };
let t2 = Task { priority: 2, id: 1 };
// `t1 < t2` is true because `priority: 1 < priority: 2`, regardless of `id`:
println!("t1 < t2: {}", t1 < t2);
}
t1 < t2: true
How #[derive] Interacts with Generic Structs
When you write #[derive(Clone, Debug)] on a generic struct struct Wrapper<T>(T), the macro expands to a conditionally bounded implementation:
// Generated automatically by `#[derive(Clone)]` on `struct Wrapper<T>(T)`:
// impl<T: Clone> Clone for Wrapper<T> { ... }
This means Wrapper<T> implements Clone whenever T implements Clone, and still compiles cleanly even when instantiated with a type T that does not implement Clone.