Rustlers Atom 2.2: `Copy` and `Clone` traits

A type that implements Copy behaves much like the old-school C integers: when you write let b = a;, the value is copied rather than moved; passing it to a function copies it; returning it from a function copies it as well; and in all these cases, the original value is still valid and can continue to be used. All the primitive scalar types you met in chapter 1 are Copy: this includes integers like i32, u64, and usize, floating-point numbers like f32 and f64, as well as bool and char. Raw pointers are also Copy, and, importantly for everyday Rust code, so are references: any &T is a Copy type.

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

fn main() {
    let p1 = Point { x: 3, y: 4 };
    let p2 = p1;           // copy
    println!("p1 = {:?}, p2 = {:?}", p1, p2);
}

You rarely implement Copy by hand; instead, you derive it for small, plain data structs. Two important rules for Copy:

No custom destructor: A type that implements Drop (cleanup logic) cannot be Copy.

All fields must be Copy: If a struct has a String inside, it can’t be Copy.

That’s why String and Vec<T> are not Copy: they manage heap memory; if you copied them implicitly, you’d easily build double-frees or subtle bugs. If in doubt, Rust tends to say “not Copy”. You can always add .clone() where it’s really needed.

Clone: explicit duplication

Clone is a trait with one method. It is explicit and potentially expensive. You decide when to pay for a duplicate. Most standard library types implement Clone.

fn main() {
    let s1 = String::from("Hello");
    let s2 = s1.clone(); // new, independent String
    println!("{s1} / {s2}");

    let v1 = vec![1, 2, 3];
    let v2 = v1.clone(); // copy the whole vector
    println!("{:?} / {:?}", v1, v2);
}

Again, you usually don’t implement Clone by hand; you derive it. The derive just stitches together clone() calls for each field.

#[derive(Clone, Debug)]
struct Config {
    retries: u8,
    name: String,
}

fn main() {
    let base = Config {
        retries: 3,
        name: String::from("service-A"),
    };

    let mut tuned = base.clone();
    tuned.retries = 5;

    println!("base  = {:?}", base);
    println!("tuned = {:?}", tuned);
}

Copy vs Clone in practice

fn main() {
    // Copy type
    let a = 10;
    let b = a;      // implicit copy
    println!("{a}, {b}"); // both valid

    // Clone-only type
    let s1 = String::from("hi");
    // let s2 = s1;       // move, s1 invalid
    let s2 = s1.clone();  // explicit clone
    println!("{s1}, {s2}");

    // Custom Copy + Clone type
    #[derive(Copy, Clone)]
    struct Pair { x: i32, y: i32 }

    let p1 = Pair { x: 1, y: 2 };
    let p2 = p1;          // copy
    let p3 = p1.clone();  // also fine, but redundant
    println!("{} {} {}", p1.x, p2.x, p3.x);
}

You'll only receive email when they publish something new.

More from GSLF
All posts