Ownership is the idea that gives Rust memory safety without a garbage collector; everything else builds on it.
Rust manages memory with three rules: each value has exactly one owner (a variable or a field); there is one owner at a time; when the owner goes out of scope, the value is dropped and its resources are freed. No garbage collector, no manual free.
Consider String, which owns a heap buffer. let s2 = s1; does not copy the buffer. It moves ownership to s2, and s1 becomes unusable; using it afterwards is a compile error ("borrow of moved value"). If both stayed valid, both would try to free the same buffer at scope end. Passing a value to a function moves it too, and returning a value moves it out to the caller.
When you really want a second independent copy, call .clone(). Clones are explicit precisely because they can be expensive, so they are easy to spot in review.
Small types that live entirely on the stack, such as integers, floats, bool, char and tuples or arrays of them, implement `Copy`. Assigning them copies the bits, and the original stays usable. Anything that owns a heap resource (String, Vec, Box) is not Copy.
Moves are checked at compile time: the compiler tracks, per path through your code, whether a variable may have been moved. A variable moved in only one branch of an if is treated as moved afterwards.
Constantly moving values in and out of functions would be tedious, which is why the next lesson introduces borrowing.
Example
fn take(s: String) -> usize {
s.len() // s is dropped here; its heap buffer is freed
}
fn main() {
let a = String::from("hello");
let b = a; // move: a is no longer valid
// println!("{a}"); // error[E0382]: borrow of moved value: `a`
let c = b.clone(); // explicit deep copy
let n = take(b); // b moved into the function
println!("{c} {n}"); // hello 5
let x = 5;
let y = x; // i32 is Copy: x is still usable
println!("{x} {y}");
}Remember
- Each value has a single owner and is dropped when the owner goes out of scope
- Assignment and passing by value move non-
Copytypes .clone()makes an explicit deep copy; simple scalars areCopy
Watch out for
- Sprinkling
.clone()everywhere to silence the borrow checker instead of borrowing.