The Default & Clone Traits

Reviewed & published by Brayan K

The Default trait supplies a standard starting value for a type via Default::default(), while the Clone trait provides an explicit .clone() method that makes an independent copy, including deep-copying heap data.

Part of the free Rust course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

In this lesson you'll derive and hand-write Default, use the ..Default::default() struct update syntax, and learn exactly how Clone differs from Copy.

What You'll Learn in This Lesson

1️⃣ Deriving Default

Add #[derive(Default)] and the compiler writes a default() that sets every field to its own type's default: 0 for numbers, false for bool, an empty String, an empty Vec, and so on. Build the value with Type::default() or Default::default().

// #[derive(Default)] gives every field its type's default value:
// 0 for numbers, false for bool, "" for String, empty for Vec, etc.
#[derive(Debug, Default)]
struct ServerConfig {
    host: String,
    port: u16,
    max_connections: u32,
    use_tls: bool,
}

fn main() {
    // Default::default() (or Type::default()) builds the all-defaults value.
    let config = ServerConfig::default();
    println!("{:?}", config);

    // The fully-qualified form also works and is handy when the type is inferred.
    let other: ServerConfig = Default::default();
    println!("{:?}", other);
}

Both forms produce the same value. Use Type::default() when you want to be explicit, and the bare Default::default() when the target type is already known from context.

2️⃣ Custom Defaults and the Update Syntax

When zero isn't the right starting value, implement Default by hand — it's just fn default() -> Self returning your chosen values. Then the ..Default::default() struct update syntax lets you set a few fields explicitly and fill the rest from the default.

// The struct update syntax `..Default::default()` fills the fields you DON'T
// set with their defaults. You can also implement Default by hand for custom
// (non-zero) defaults.
#[derive(Debug)]
struct Settings {
    volume: u8,
    brightness: u8,
    night_mode: bool,
}

// Manual impl: choose sensible non-default defaults.
impl Default for Settings {
    fn default() -> Self {
        Settings {
            volume: 50,        // not 0
            brightness: 80,    // not 0
            night_mode: false,
        }
    }
}

fn main() {
    // Start from the defaults, override only what we care about.
    let custom = Settings {
        volume: 100,
        ..Default::default()    // brightness=80, night_mode=false come from default
    };
    println!("{:?}", custom);

    // The plain defaults, untouched.
    println!("{:?}", Settings::default());
}

3️⃣ Clone vs Copy

Copy is implicit and cheap: assignment duplicates the value bit for bit and the original stays valid, but it's only allowed when every field is Copy. Clone is explicit: you call .clone(), and it can do real work like deep-copying a heap String. Any type with a String or Vec field can be Clone but not Copy.

// Clone vs Copy.
// Copy: implicit, cheap, bitwise — only for types whose fields are all Copy.
// Clone: explicit .clone(), can deep-copy heap data like String/Vec.
#[derive(Debug, Clone, Copy)]
struct PointCopy { x: i32, y: i32 }     // all-Copy fields -> can be Copy

#[derive(Debug, Clone)]                 // has a String -> Clone only, NOT Copy
struct Person { name: String, age: u32 }

fn main() {
    // Copy type: assignment duplicates, original stays valid (no .clone needed).
    let p = PointCopy { x: 1, y: 2 };
    let q = p;                           // copied
    println!("p = {:?}, q = {:?}", p, q);

    // Clone-only type: must call .clone() to get an independent copy.
    let a = Person { name: String::from("Ada"), age: 36 };
    let b = a.clone();                   // deep copy of the heap String
    println!("a = {:?}", a);
    println!("b = {:?}", b);
}

PointCopy duplicates silently on let q = p;. Person owns a heap String, so it needs an explicit .clone() to make an independent copy. Every Copy type must also implement Clone.

Your turn. Fill in the blank marked ___, then run it.

// 🎯 YOUR TURN — use struct update syntax to fill the rest from defaults.
#[derive(Debug, Default)]
struct Profile {
    username: String,
    score: u32,
    active: bool,
}

fn main() {
    let p = Profile {
        username: String::from("neo"),
        ___                          // 👉 fill remaining fields from Default
    };
    println!("{:?}", p);
}

// ✅ Expected output:
//    Profile { username: "neo", score: 0, active: false }

Mini-Challenge: Game State with Custom Defaults

Hand-write Default for a game state, then clone it and mutate the copy. Run it with cargo run and check the output.

// 🎯 MINI-CHALLENGE: a game state with custom defaults and a save/clone.
//
// 1. Define struct GameState { level: u32, lives: u32, player: String,
//    inventory: Vec<String> } and derive Debug and Clone.
// 2. impl Default for GameState by hand: level 1, lives 3, player "Player1",
//    empty inventory.
// 3. In main, make a default state and print it. Then .clone() it into a
//    mutable "saved", set saved.level = 5, push "sword" onto saved.inventory,
//    and print both saved and the original to show the original is unchanged.
//
// ✅ Example output:
//    start: GameState { level: 1, lives: 3, player: "Player1", inventory: [] }
//    saved: GameState { level: 5, lives: 3, player: "Player1", inventory: ["sword"] }
//    start: GameState { level: 1, lives: 3, player: "Player1", inventory: [] }

fn main() {
    // your code here
}

Common Errors (and the fix)

Pro Tips

📋 Quick Reference — Default & Clone

FormMeaning
Type::default()build the default value
..Default::default()fill remaining fields
impl Defaultcustom non-zero defaults
x.clone()explicit deep copy
let y = x; // Copyimplicit copy, x still valid

🎉 Lesson Complete!

Practice quiz

What single method does the Default trait provide?

  • new()
  • init()
  • default()
  • empty()

Answer: default(). Default provides default(), which returns a sensible starting value for the type.

With #[derive(Default)], what value does a numeric field get?

  • 0
  • 1
  • -1
  • Its maximum

Answer: 0. Derived Default gives each field its type's default: 0 for numbers, false for bool, empty String/Vec.

What does #[derive(Default)] give a bool field and a String field?

  • true and a space
  • false and null
  • 1 and "0"
  • false and an empty String

Answer: false and an empty String. A bool defaults to false and a String defaults to an empty String.

What does ..Default::default() do in a struct literal?

  • Resets all fields to zero
  • Fills the fields you didn't set from the default
  • Calls a constructor
  • Overrides every field

Answer: Fills the fields you didn't set from the default. It fills every remaining (unset) field from the type's Default implementation.

Where must ..Default::default() appear in a struct literal?

  • Last
  • First
  • Anywhere
  • Only alone

Answer: Last. The .. spread must come last; it only fills the fields you didn't already set.

When should you implement Default by hand instead of deriving it?

  • Never
  • Only for enums
  • When a field needs a non-zero default like volume 50
  • When the struct has one field

Answer: When a field needs a non-zero default like volume 50. Hand-write Default when zero/empty isn't right, e.g. a volume that should start at 50 or a port at 8080.

What is the real difference between Copy and Clone?

  • They are identical
  • Copy is implicit and bitwise; Clone is explicit via .clone() and can deep-copy
  • Clone is implicit; Copy is explicit
  • Copy works only on strings

Answer: Copy is implicit and bitwise; Clone is explicit via .clone() and can deep-copy. Copy duplicates implicitly with a bitwise copy; Clone is an explicit .clone() that can deep-copy heap data.

Why can a struct with a String field be Clone but not Copy?

  • String is not a type
  • Copy is deprecated
  • Clone forbids strings
  • String owns heap data, which isn't trivially bitwise-copyable

Answer: String owns heap data, which isn't trivially bitwise-copyable. Copy requires all fields be Copy; String owns heap memory, so the struct can only be Clone.

After let q = p; where p is a Copy type, is p still valid?

  • No, it was moved
  • Yes, Copy types duplicate so the original stays valid
  • Only if you call .clone()
  • Only inside a loop

Answer: Yes, Copy types duplicate so the original stays valid. Copy duplicates the value on assignment, so the original p remains usable.

Which relationship between Copy and Clone always holds?

  • Clone requires Copy
  • They are mutually exclusive
  • Every Copy type must also implement Clone
  • Neither requires the other

Answer: Every Copy type must also implement Clone. Every Copy type must also implement Clone, so they are derived together: #[derive(Clone, Copy)].

Continue this course

Frequently asked questions

What does the Default trait give my type?

Default provides a single method, default(), that returns a sensible 'empty' or starting value for the type. When you derive it with #[derive(Default)], each field is set to its own type's default: 0 for numeric types, false for bool, an empty String, an empty Vec, and so on. You then create the value with Type::default() or, when the type can be inferred, Default::default(). It is the idiomatic way to express a zero-configuration starting point, and many standard library APIs rely on it.

How does the ..Default::default() struct update syntax work?

When you build a struct, you can set just the fields you care about and then write ..Default::default() at the end to fill in every remaining field from the type's Default implementation. For example Settings { volume: 100, ..Default::default() } sets volume explicitly and takes brightness and night_mode from default(). This keeps construction concise when a struct has many fields but you only want to override a few. The same .. syntax also works with any existing value, not only default().

When should I implement Default manually instead of deriving it?

Derive Default when the all-zero, all-empty value is genuinely the right starting point. Implement it by hand when a field needs a non-zero default — for example a volume that should start at 50, a retry count that should start at 3, or a port that should default to 8080. A manual impl is just fn default() -> Self that returns the struct with your chosen values. You can mix approaches across types freely; the choice is per type.

What is the real difference between Clone and Copy?

Copy means a value can be duplicated with a trivial bitwise copy and is duplicated implicitly on assignment or when passed to a function, leaving the original valid. It is only allowed when every field is itself Copy, which rules out heap-owning types like String and Vec. Clone is explicit: you call .clone(), and it can do real work such as allocating and deep-copying heap data, so it works for String, Vec, and structs containing them. In short, Copy is implicit and cheap for simple stack data, while Clone is explicit and can be more expensive. Every Copy type must also implement Clone.