Rustlers Atom 2.10: Move vs Clone Semantics

Understanding the memory layout of growable collections like String and Vec is the key to understanding why Moves are fast and Clones are expensive.

A String (or Vec) is made of three parts stored on the Stack:

  1. Pointer: Points to the data on the heap.
  2. Length: How much data is currently used.
  3. Capacity: How much space is allocated before we need to ask the OS for more.

The actual data (the letters of the string) lives on the Heap.

We can prove how this works by inspecting the memory address of the heap data. Notice how move preserves the address (same data, new owner), while clone creates a new address (new data).

fn main() {
    // 1. Creation
    let s1 = String::from("Rust");
    println!("s1 ptr: {:p}", s1.as_ptr()); // Prints address, e.g., 0x7f...10

    // 2. Move
    // Only the stack metadata (ptr, len, cap) is copied. 
    // The heap buffer stays exactly where it is.
    let s2 = s1; 
    // println!("{:p}", s1.as_ptr()); // ❌ Error: s1 is gone
    println!("s2 ptr: {:p}", s2.as_ptr()); // Prints SAME address: 0x7f...10

    // 3. Clone
    // A deep copy happens. New heap memory is allocated.
    let s3 = s2.clone();
    println!("s3 ptr: {:p}", s3.as_ptr()); // Prints NEW address: 0x7f...99
}
  • Move Cost: $O(1) (instant). It just copies three numbers on the stack.
  • Clone Cost: O(n) (linear). It forces memory allocation and copies every byte.

Rust defaults to Move to ensure performance. You must opt-in to Clone so you remain aware of the cost.


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

More from GSLF
All posts