Ownership and borrowing · Lesson 11
Clone and Copy
cargo run --bin 11_clone_copyAssigning or passing a value normally moves it. Two traits change that:
Copy: an implicit bitwise duplicate. The old name stays usable after `let b = a`. Only for types where a memcpy is a correct copy: integers, floats,bool,char, shared refs, and tuples/arrays/structs made only ofCopyparts.Clone: an explicit`.clone()`that may do real work (allocate, bump a refcount).
Every Copy type must also be Clone. A type cannot be Copy if it implements Drop or owns heap data (String, Vec, Box).
Built-in Copy types stay usable after assignment
fn demo_builtin_copy() {
println!("-- builtin copy --");
let a = 5;
let b = a;
let c = 'x';
let d = c;
let t = (1, true, 2.5);
let u = t;
let arr = [1u8; 4];
let arr2 = arr;
println!("{a} {b} {c} {d} {t:?} {u:?} {arr:?} {arr2:?}");
}A struct made of Copy fields can derive Copy
#[derive(Debug, Clone, Copy, PartialEq)]
struct Point {
x: i32,
y: i32,
}
fn shift(mut p: Point) -> Point {
p.x += 10;
p
}String is Clone but not Copy. Assignment moves; clone duplicates the heap
fn demo_derive_copy() {
println!("-- derive copy --");
let a = Point { x: 1, y: 2 };
let b = shift(a); // a is copied into shift, not moved
println!("a {a:?} b {b:?} equal? {}", a == b);
}
// buffer.
fn demo_clone_heap() {
println!("-- clone heap --");
let s1 = String::from("hello");
let s2 = s1.clone();
let s3 = s1; // move: s1 is no longer usable
// println!("{s1}"); // error[E0382]: borrow of moved value: `s1`
println!("{s2} {s3}");
let v = vec![vec![1, 2], vec![3]];
let mut w = v.clone(); // deep: the inner Vecs are cloned too
w[0].push(99);
println!("v {v:?} w {w:?}");
}A struct with a String field can derive Clone, but not Copy
#[derive(Debug, Clone)]
struct User {
name: String,
age: u32,
}
// #[derive(Clone, Copy)]
// struct Bad { name: String }
// error[E0204]: the trait `Copy` cannot be implemented for this type
fn demo_clone_struct() {
println!("-- clone struct --");
let u1 = User { name: "ana".into(), age: 30 };
let mut u2 = u1.clone();
u2.name.push_str("_copy");
u2.age += 1;
println!("{u1:?} {u2:?}");
}Clone does not have to be deep. Rc::clone only bumps a counter, and both
// handles point at the same data.
fn demo_cheap_clone() {
println!("-- cheap clone --");
let a = Rc::new(String::from("shared"));
let b = Rc::clone(&a);
println!("{a} {b} count {} same? {}", Rc::strong_count(&a), Rc::ptr_eq(&a, &b));
}A hand-written Clone, for when derive does the wrong thing
#[derive(Debug)]
struct Counter {
id: u32,
hits: u32,
}
impl Clone for Counter {
// A clone keeps the id but starts with zero hits.
fn clone(&self) -> Self {
Counter { id: self.id, hits: 0 }
}
}
fn demo_manual_clone() {
println!("-- manual clone --");
let a = Counter { id: 7, hits: 42 };
let b = a.clone();
println!("{a:?} {b:?} hits {} -> {}", a.hits, b.hits);
}Drop and Copy do not mix. A Drop type runs cleanup once, so silent
// duplicates would run it twice.
struct Guard(&'static str);
impl Drop for Guard {
fn drop(&mut self) {
println!("drop {}", self.0);
}
}
// impl Copy for Guard {}
// error[E0184]: the trait `Copy` cannot be implemented for this type; the type has a
// destructor
fn demo_drop_not_copy() {
println!("-- drop not copy --");
let g = Guard("guard");
let moved = g; // a move, so drop runs exactly once at the end of scope
let _ = &moved;
}Copied vs cloned on iterators turn &T into T
fn demo_iter_copied_cloned() {
println!("-- copied / cloned --");
let nums = [3, 1, 2];
let owned: Vec<i32> = nums.iter().copied().collect();
let words = vec![String::from("a"), String::from("b")];
let owned_words: Vec<String> = words.iter().cloned().collect();
println!("{owned:?} {owned_words:?} (words still here: {words:?})");
}$ cargo run --bin 11_clone_copy -- builtin copy -- 5 5 x x (1, true, 2.5) (1, true, 2.5) [1, 1, 1, 1] [1, 1, 1, 1] -- derive copy -- a Point { x: 1, y: 2 } b Point { x: 11, y: 2 } equal? false -- clone heap -- hello hello v [[1, 2], [3]] w [[1, 2, 99], [3]] -- clone struct -- User { name: "ana", age: 30 } User { name: "ana_copy", age: 31 } -- cheap clone -- shared shared count 2 same? true -- manual clone -- Counter { id: 7, hits: 42 } Counter { id: 7, hits: 0 } hits 42 -> 0 -- drop not copy -- drop guard -- copied / cloned -- [3, 1, 2] ["a", "b"] (words still here: ["a", "b"])
Full program
// 11. Clone and Copy
//
// Assigning or passing a value normally moves it. Two traits change that:
//
// - Copy: an implicit bitwise duplicate. The old name stays usable after `let b = a`.
// Only for types where a memcpy is a correct copy: integers, floats, bool, char,
// shared refs, and tuples/arrays/structs made only of Copy parts.
// - Clone: an explicit `.clone()` that may do real work (allocate, bump a refcount).
//
// Every Copy type must also be Clone. A type cannot be Copy if it implements Drop or
// owns heap data (String, Vec, Box).
//
// Run: cargo run --bin 11_clone_copy
use std::rc::Rc;
// Example 1: built-in Copy types stay usable after assignment.
fn demo_builtin_copy() {
println!("-- builtin copy --");
let a = 5;
let b = a;
let c = 'x';
let d = c;
let t = (1, true, 2.5);
let u = t;
let arr = [1u8; 4];
let arr2 = arr;
println!("{a} {b} {c} {d} {t:?} {u:?} {arr:?} {arr2:?}");
}
// Example 2: a struct made of Copy fields can derive Copy.
#[derive(Debug, Clone, Copy, PartialEq)]
struct Point {
x: i32,
y: i32,
}
fn shift(mut p: Point) -> Point {
p.x += 10;
p
}
fn demo_derive_copy() {
println!("-- derive copy --");
let a = Point { x: 1, y: 2 };
let b = shift(a); // a is copied into shift, not moved
println!("a {a:?} b {b:?} equal? {}", a == b);
}
// Example 3: String is Clone but not Copy. Assignment moves; clone duplicates the heap
// buffer.
fn demo_clone_heap() {
println!("-- clone heap --");
let s1 = String::from("hello");
let s2 = s1.clone();
let s3 = s1; // move: s1 is no longer usable
// println!("{s1}"); // error[E0382]: borrow of moved value: `s1`
println!("{s2} {s3}");
let v = vec![vec![1, 2], vec![3]];
let mut w = v.clone(); // deep: the inner Vecs are cloned too
w[0].push(99);
println!("v {v:?} w {w:?}");
}
// Example 4: a struct with a String field can derive Clone, but not Copy.
#[derive(Debug, Clone)]
struct User {
name: String,
age: u32,
}
// #[derive(Clone, Copy)]
// struct Bad { name: String }
// error[E0204]: the trait `Copy` cannot be implemented for this type
fn demo_clone_struct() {
println!("-- clone struct --");
let u1 = User { name: "ana".into(), age: 30 };
let mut u2 = u1.clone();
u2.name.push_str("_copy");
u2.age += 1;
println!("{u1:?} {u2:?}");
}
// Example 5: Clone does not have to be deep. Rc::clone only bumps a counter, and both
// handles point at the same data.
fn demo_cheap_clone() {
println!("-- cheap clone --");
let a = Rc::new(String::from("shared"));
let b = Rc::clone(&a);
println!("{a} {b} count {} same? {}", Rc::strong_count(&a), Rc::ptr_eq(&a, &b));
}
// Example 6: a hand-written Clone, for when derive does the wrong thing.
#[derive(Debug)]
struct Counter {
id: u32,
hits: u32,
}
impl Clone for Counter {
// A clone keeps the id but starts with zero hits.
fn clone(&self) -> Self {
Counter { id: self.id, hits: 0 }
}
}
fn demo_manual_clone() {
println!("-- manual clone --");
let a = Counter { id: 7, hits: 42 };
let b = a.clone();
println!("{a:?} {b:?} hits {} -> {}", a.hits, b.hits);
}
// Example 7: Drop and Copy do not mix. A Drop type runs cleanup once, so silent
// duplicates would run it twice.
struct Guard(&'static str);
impl Drop for Guard {
fn drop(&mut self) {
println!("drop {}", self.0);
}
}
// impl Copy for Guard {}
// error[E0184]: the trait `Copy` cannot be implemented for this type; the type has a
// destructor
fn demo_drop_not_copy() {
println!("-- drop not copy --");
let g = Guard("guard");
let moved = g; // a move, so drop runs exactly once at the end of scope
let _ = &moved;
}
// Example 8: copied vs cloned on iterators turn &T into T.
fn demo_iter_copied_cloned() {
println!("-- copied / cloned --");
let nums = [3, 1, 2];
let owned: Vec<i32> = nums.iter().copied().collect();
let words = vec![String::from("a"), String::from("b")];
let owned_words: Vec<String> = words.iter().cloned().collect();
println!("{owned:?} {owned_words:?} (words still here: {words:?})");
}
fn main() {
demo_builtin_copy();
demo_derive_copy();
demo_clone_heap();
demo_clone_struct();
demo_cheap_clone();
demo_manual_clone();
demo_drop_not_copy();
demo_iter_copied_cloned();
}