Can you write this from memory?
Add the derive attribute to enable debug printing for a struct.
Rust's #[derive] attribute automatically implements common traits for your types. Instead of writing boilerplate, you let the compiler generate it.
But which traits can be derived? When should you derive vs implement manually? What are the requirements for each?
This page focuses on the derive syntax and semantics you need to know.
Copy requires every field to be Copy (stack-only, no heap data). Types with String, Vec, or Box fields — or types implementing Drop — cannot be Copy:
// WRONG: String is heap-allocated, not Copy
// #[derive(Copy, Clone)]
// struct User {
// name: String, // error[E0204]
// age: u32,
// }
// RIGHT: Use Clone without Copy
#[derive(Clone)]
struct User {
name: String,
age: u32,
}
// Copy is fine for stack-only types
#[derive(Copy, Clone)]
struct Point {
x: f64,
y: f64,
}
Rule of thumb: derive Copy on small value types (coordinates, colors, flags). Use Clone for everything else. The traits and generics guide covers how derive macros generate trait implementations under the hood.
HashMap requires keys to implement all three traits. Missing any one produces a compile error:
// WRONG: Hash without Eq
// #[derive(Hash)]
// struct Key { id: u32 }
// let map: HashMap<Key, String>; // error[E0277]: Key doesn't implement Eq
// RIGHT: Derive the complete bundle
#[derive(Debug, Hash, PartialEq, Eq)]
struct Key {
id: u32,
name: String,
}
let mut map = std::collections::HashMap::new();
map.insert(Key { id: 1, name: "a".into() }, "value".into());
The bundle is always Hash + PartialEq + Eq. If your key contains floats, you cannot derive Eq (see Ord/PartialOrd below).
| Trait | What it does | Field requirement | Notes |
|---|---|---|---|
Debug | {:?} formatting | All fields: Debug | Derive on almost everything |
Clone | Explicit .clone() | All fields: Clone | |
Copy | Implicit bitwise copy | All fields: Copy + no Drop | Stack-only types |
PartialEq | == and != | All fields: PartialEq | Compares all fields |
Eq | Reflexivity marker | Requires PartialEq | No extra code — just a guarantee |
Hash | HashMap/HashSet keys | All fields: Hash | Derive with PartialEq + Eq |
Default | Default::default() | All fields: Default | Gives 0, false, "" |
PartialOrd | <, >, <=, >= | All fields: PartialOrd | Compares fields in definition order |
Ord | Total ordering | Requires PartialOrd + Eq | Needed for BTreeMap keys |
Derived Default works on enums by marking one unit variant with #[default]:
#[derive(Default, Debug)]
enum Status {
#[default]
Pending,
Active,
Inactive,
}
let status = Status::default();
println!("{status:?}"); // Pending
Exactly one variant must be marked #[default], and it must be a unit variant (no fields).
Derived ordering compares fields in definition order (first field, then second as tiebreaker, etc.):
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Score {
points: i32,
name: String,
}
let mut scores = vec![
Score { points: 90, name: "Alice".into() },
Score { points: 85, name: "Bob".into() },
];
scores.sort(); // sorts by points, then name
Float gotcha: f32 and f64 implement PartialOrd but not Ord, because NaN != NaN breaks total ordering. You cannot derive Ord on types containing floats:
// WRONG: f64 is not Ord
// #[derive(Ord, PartialOrd, PartialEq, Eq)]
// struct Measurement { value: f64 } // error[E0277]
// Workaround: use the ordered-float crate, or implement Ord manually
// with a NaN policy (e.g., treat NaN as greater than all values)
When you derive a trait on a generic struct, the compiler adds trait bounds to the generated impl:
#[derive(Clone, Debug)]
struct Wrapper<T>(T);
// The compiler generates:
// impl<T: Clone> Clone for Wrapper<T> { ... }
// impl<T: Debug> Debug for Wrapper<T> { ... }
This means Wrapper<T> is only Clone when T is Clone. If you need different bounds, implement the trait manually. For the full trait bound syntax, see the traits cheat sheet.
Derive uses "all fields" semantics — it includes every field in the comparison, hash, debug output, etc. The structs and enums guide covers how field definitions work alongside derive macros. Implement manually when:
- Equality should ignore some fields — e.g., skip internal IDs, caches, or timestamps
- Debug should hide sensitive data — e.g., mask passwords or tokens
- Default needs non-zero values — e.g., a config with
retries: 3instead of0 - Clone needs special logic — e.g., resetting a counter on clone
// Manual PartialEq: compare only the meaningful fields
struct User {
id: u64, // internal, skip
name: String, // compare this
email: String, // compare this
}
impl PartialEq for User {
fn eq(&self, other: &Self) -> bool {
self.name == other.name && self.email == other.email
}
}
impl Eq for User {}
// Struct with common derives
#[derive(Debug, Clone, PartialEq)]
struct Point {
x: i32,
y: i32,
}
// Tuple struct as a newtype
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Color(u8, u8, u8);
// Struct with Default
#[derive(Default)]
struct Config {
verbose: bool, // defaults to false
count: i32, // defaults to 0
name: String, // defaults to ""
}
let config = Config::default();
// Enum with Debug
#[derive(Debug)]
enum Status {
Active,
Inactive,
Pending(String),
}
- The Rust Book: Derivable Traits — all standard derivable traits
- Rust Reference: Derive — how derive macros expand
- std::marker::Copy — Copy requirements and E0204
- std::default::Default — Default on enums with
#[default]
When to Use Rust Derive Traits: Debug, Clone, PartialEq & More
- Derive `Debug` on almost everything for debugging.
- Derive `Clone` when you need explicit duplication.
- Derive `Copy` only for small, stack-only types without heap data.
- Derive `PartialEq` + `Eq` for types used in comparisons.
- Derive `Hash` alongside `Eq` for HashMap keys.
- Derive `Default` for types with sensible zero/empty defaults.
Check Your Understanding: Rust Derive Traits: Debug, Clone, PartialEq & More
What is the difference between Clone and Copy in Rust?
Clone is explicit duplication via .clone() method—can be expensive. Copy is implicit bitwise copy on assignment—only for simple types without heap data. Copy types are always Clone, but not vice versa.
What You'll Practice: Rust Derive Traits: Debug, Clone, PartialEq & More
Common Rust Derive Traits: Debug, Clone, PartialEq & More Pitfalls
- Symptom: `error[E0204]: the trait Copy cannot be implemented for this type`. Why: You derived Copy on a struct with String, Vec, or another heap-allocated field. Copy is only for stack-only types. Fix: Remove Copy from the derive list and use Clone instead. If you need value semantics, call `.clone()` explicitly.
- Symptom: `error[E0277]: MyStruct doesn't implement Debug`. Why: You tried to print with `{:?}` or use `dbg!()` but never derived Debug. Fix: Add `#[derive(Debug)]`. Do this on virtually every struct and enum you create.
- Symptom: `error[E0277]: the trait bound MyStruct: Hash is not satisfied`. Why: You derived Hash but forgot PartialEq and Eq. HashMap keys require all three. Fix: Change to `#[derive(Hash, PartialEq, Eq)]`.
- Symptom: Derived PartialEq but equality checks give unexpected results. Why: Derived PartialEq compares ALL fields, including ones you might consider internal (IDs, timestamps, caches). Fix: Implement PartialEq manually to compare only the fields that define logical equality.
- Symptom: `Default::default()` produces surprising values. Why: Derived Default gives `0`, `false`, `""` for each field — which may not be a valid state for your type. Fix: Implement Default manually when zero-values are not sensible defaults.
Rust Derive Traits: Debug, Clone, PartialEq & More FAQ
Why derive Debug on everything?
Debug enables `{:?}` and `{:#?}` formatting, essential for debugging with `println!` and `dbg!`. There is almost no reason not to derive it.
When should I implement a trait manually instead of deriving?
When you need custom behavior. For example, PartialEq that ignores certain fields, or Debug that hides sensitive data. Derive uses all fields; manual impl gives you control.
Why does Copy require Clone?
Copy is a marker trait indicating bitwise copy semantics. Clone provides the explicit .clone() method. Every Copy type can trivially implement Clone, so Rust requires both.
Can I derive traits on enums?
Yes. For most traits, all variants (and their fields) must also implement the trait. Works the same as structs.
What types cannot be Copy?
Types with heap data (String, Vec, Box), types implementing Drop, and types containing non-Copy fields. Essentially, anything that needs cleanup or has ownership semantics.
Rust Derive Traits: Debug, Clone, PartialEq & More Syntax Quick Reference
#[derive(Debug)]
struct Foo { x: i32 }#[derive(Debug, Clone, PartialEq)]#[derive(Debug, Clone, Copy)]
struct Point(i32, i32);#[derive(Debug, Clone, PartialEq, Eq, Hash)]#[derive(Default)]
struct Config { verbose: bool }#[derive(Debug, Clone, PartialEq)]
enum Status { On, Off }println!("{:?}", my_struct);let copy = original.clone();let config = Config::default();Rust Derive Traits: Debug, Clone, PartialEq & More Sample Exercises
Fill in the derive attribute to make Person printable with {:?}.
#[derive(Debug)]Fill in the trait to enable {:?} formatting.
DebugFill in the trait to enable explicit cloning.
Clone+ 46 more exercises