Syntax Cache
BlogMethodFeaturesHow It WorksBuild a Game
  1. Home
  2. Rust
  3. Rust Derive Traits Practice: Debug, Clone, PartialEq & More
Rust49 exercises

Rust Derive Traits Practice: Debug, Clone, PartialEq & More

Practice Rust derive macros including Debug, Clone, Copy, PartialEq, Eq, Hash, Default, and understand when to derive vs implement manually.

Cheat SheetCommon ErrorsQuick ReferencePractice
Warm-up1 / 2

Can you write this from memory?

Add the derive attribute to enable debug printing for a struct.

On this page
  1. 1Compiler Error E0204: Copy Cannot Be Implemented
  2. 2HashMap Keys: Derive Hash + PartialEq + Eq
  3. 3The Derive Cheat Sheet
  4. 4Default on Enums: #[default]
  5. 5Ord, PartialOrd, and the Float Gotcha
  6. 6Derive on Generic Types Adds Bounds
  7. 7Derive vs Manual Impl
  8. 8Core Syntax Patterns
  9. 9Further Reading
Compiler Error E0204: Copy Cannot Be ImplementedHashMap Keys: Derive Hash + PartialEq + EqThe Derive Cheat SheetDefault on Enums: #[default]Ord, PartialOrd, and the Float GotchaDerive on Generic Types Adds BoundsDerive vs Manual ImplCore Syntax PatternsFurther Reading

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.

Related Rust Topics
Rust Structs & Enums: Custom Types, Option & ResultRust Traits & Generics: Trait Bounds, impl Trait, TurbofishRust Macros: macro_rules!, Declarative Macros

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.

Ready to practice?

Start practicing Rust Derive Traits: Debug, Clone, PartialEq & More with spaced repetition

HashMap keys require all three traits: Hash + PartialEq + Eq. Missing any one produces a compile error. If your key contains floats, you cannot derive Eq.

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).

Derive Debug on almost everything. Derive Clone for explicit duplication. Copy is only for small stack-only types. HashMap keys need the full bundle: Hash + PartialEq + Eq.

TraitWhat it doesField requirementNotes
Debug{:?} formattingAll fields: DebugDerive on almost everything
CloneExplicit .clone()All fields: Clone
CopyImplicit bitwise copyAll fields: Copy + no DropStack-only types
PartialEq== and !=All fields: PartialEqCompares all fields
EqReflexivity markerRequires PartialEqNo extra code — just a guarantee
HashHashMap/HashSet keysAll fields: HashDerive with PartialEq + Eq
DefaultDefault::default()All fields: DefaultGives 0, false, ""
PartialOrd<, >, <=, >=All fields: PartialOrdCompares fields in definition order
OrdTotal orderingRequires PartialOrd + EqNeeded 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. Implement manually when equality should ignore some fields, Debug should hide sensitive data, or Default needs non-zero values.

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: 3 instead of 0
  • 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

Prompt

What is the difference between Clone and Copy in Rust?

What a strong answer looks like

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

Use #[derive(...)] syntaxDerive Debug for debugging outputDerive Clone for explicit duplicationDerive Copy for implicit copyingDerive PartialEq and Eq for comparisonsDerive Hash for HashMap keysDerive Default for default valuesUnderstand trait requirements and limitations

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

Basic derive
#[derive(Debug)]
struct Foo { x: i32 }
Multiple traits
#[derive(Debug, Clone, PartialEq)]
Copy type
#[derive(Debug, Clone, Copy)]
struct Point(i32, i32);
HashMap key
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
Default struct
#[derive(Default)]
struct Config { verbose: bool }
Enum derive
#[derive(Debug, Clone, PartialEq)]
enum Status { On, Off }
Use Debug
println!("{:?}", my_struct);
Use Clone
let copy = original.clone();
Use Default
let config = Config::default();

Rust Derive Traits: Debug, Clone, PartialEq & More Sample Exercises

Example 1Difficulty: 1/5

Fill in the derive attribute to make Person printable with {:?}.

#[derive(Debug)]
Example 2Difficulty: 1/5

Fill in the trait to enable {:?} formatting.

Debug
Example 3Difficulty: 1/5

Fill in the trait to enable explicit cloning.

Clone

+ 46 more exercises

Further Reading

  • Rust Newtype Pattern: Catch Unit Bugs at Compile Time18 min read

Start practicing Rust Derive Traits: Debug, Clone, PartialEq & More

Free daily exercises with spaced repetition. No credit card required.

← Back to Rust Syntax Practice
Syntax Cache

Build syntax muscle memory with spaced repetition.

Product

  • Pricing
  • Our Method
  • Daily Practice
  • Design Patterns
  • Interview Prep

Resources

  • Blog
  • Compare
  • Cheat Sheets
  • Vibe Coding
  • Muscle Memory

Languages

  • Python
  • JavaScript
  • TypeScript
  • Rust
  • SQL
  • GDScript

Legal

  • Terms
  • Privacy
  • Contact

© 2026 Syntax Cache

Cancel anytime in 2 clicks. Keep access until the end of your billing period.

No refunds for partial billing periods.