On-Ramp to Rust

the move and Copy types

The ownership rule says each value has exactly one owner. But programs constantly hand values from one variable to another — you assign, you pass to a function, you return. So what happens to ownership when you write let b = a? In a language like C, copying a pointer just makes a second pointer to the same block, and now two variables both think they are in charge. Rust cannot allow that without breaking the one-owner rule. Its answer is the move: when you assign or pass a value that owns heap memory, ownership is transferred to the destination and the source is invalidated — the original variable can no longer be used.

Picture a String, which is a small record on the stack holding a pointer to heap characters, a length, and a capacity. When you write let b = a, Rust copies that small stack record into b, but it does not copy the heap characters, and crucially it marks a as no longer valid. Now b is the sole owner of the heap data and a is dead — touch a again and the compiler stops you with use of moved value. This is why it is called a move rather than a copy: ownership moved from a to b, leaving exactly one owner, so the heap memory will be freed exactly once, when b's scope ends. Passing a value to a function moves it in the same way; that is why, in the ownership example, the original variable was unusable after the call.

Not everything moves, though, and that is where Copy types come in. Small, plain values that live entirely on the stack and own no heap resource — integers, floating-point numbers, bool, char, and tuples of such things — implement a trait called Copy. For these, let b = a duplicates the bits and leaves a perfectly usable, because copying them is cheap and there is no single heap resource that two owners would fight over. So the rule of thumb is: if a value owns a resource (like String or a Vec), assigning or passing it moves it and invalidates the source; if it is a simple Copy value (like i32), it is copied and the source lives on. The honest subtlety is that move is the default and Copy is the special case — beginners are often surprised the first time a String becomes unusable after being passed to a function.

let a = String::from("hi"); let b = a; // MOVE: b owns it now, a is invalidated // println!("{a}"); // error[E0382]: borrow of moved value: `a` let x = 5; // i32 is Copy let y = x; // COPY: x is still usable println!("{x} {y}"); // prints: 5 5

A String assignment moves and invalidates the source; an i32 assignment copies and keeps it.

A move does not copy the heap data and does not run cleanup on the source — it just transfers ownership and forbids further use of the source. If you genuinely want a second independent copy of a String or Vec, you ask for it explicitly with .clone().

Also called
move semanticsmovemoving a value移動語意移動