Nugget 3

Home

Copying vs. Moving Values

You pass a value to a function and suddenly you can't use it anymore. Meet Copy and Clone — the traits that control ownership transfer.

The Problem: Moves

In Rust, assigning or passing a value moves it. The old owner can't use it anymore:

fn takes_ownership(s: String) {
    println!("Got: {}", s);
}

fn main() {
    let name = String::from("Rex");
    takes_ownership(name);
    println!("{}", name);  // ❌ borrow of moved value
}

Rust does this to prevent double-free bugs — only one owner exists at a time. But sometimes you want to keep using the original.

Clone: Explicit Duplication

Call .clone() to make an explicit copy. The original stays usable:

fn takes_ownership(s: String) {
    println!("Got: {}", s);
}

fn main() {
    let name = String::from("Rex");
    takes_ownership(name.clone());  // ✅ pass a copy, keep the original
    println!("{}", name);           // ✅ still alive!
}

💡 Clone is explicit by design

The .clone() call makes the performance cost visible in the source code. You'll never accidentally copy a huge Vec or String.

Deriving Clone

To make your own types cloneable, add #[derive(Clone)]:

#[derive(Clone)]
struct Pet {
    name: String,
    age: u8,
}

fn main() {
    let dog = Pet { name: "Rex".into(), age: 3 };
    let backup = dog.clone();  // ✅ deep copy
    println!("{}", dog.name);  // ✅ still accessible
}

Every field must itself implement Clone — Rust checks this when you derive it.

Copy: Automatic Copy

Copy is Clone's simpler sibling — types that are simple bitwise copies (like numbers and bools) can be marked Copy. Then they copy automatically when moved:

fn takes_number(n: i32) {
    println!("Got: {}", n);
}

fn main() {
    let x = 42;
    takes_number(x);  // ✅ x is i32 — it's Copy, so it gets duplicated
    println!("{}", x); // ✅ still works, no .clone() needed
}

That's why integers, booleans, and chars "just work" after being passed around — they implement Copy.

Making Your Own Type Copy

You can derive Copy too — but only if every field is also Copy, and you must also derive Clone:

#[derive(Debug, Clone, Copy)]  // Copy requires Clone
struct Point { x: i32, y: i32 }

fn midpoint(a: Point, b: Point) -> Point {
    Point {
        x: (a.x + b.x) / 2,
        y: (a.y + b.y) / 2,
    }
}

fn main() {
    let p1 = Point { x: 0, y: 0 };
    let p2 = Point { x: 10, y: 20 };

    let mid = midpoint(p1, p2);  // both passed by copy

    println!("{:?} {:?}", p1, p2); // ✅ still alive!
}

⚠️ Not everything can be Copy

Types that own heap-allocated data — String, Vec, Box — can't be Copy. A bitwise copy of a String would produce two pointers to the same heap memory, causing a double-free. Use .clone() instead.

Quick Reference

Trait How it copies Code visible? Usage
Clone Deep copy (heap-aware) Yes — call .clone() Any type
Copy Bitwise memcpy No — happens automatically Simple types only
Move (default) Transfer ownership No — silent transfer Any type without Copy

Key Takeaways