Ownership & Moves
One Owner at a Time
Section titled “One Owner at a Time”Every Rust value has exactly one owner. When you assign a value to another variable, or pass it into a function, what happens depends on whether the type implements Copy.
Non-Copy Types Move
Section titled “Non-Copy Types Move”Types that own heap memory — like String, Vec<i32>, or Box<i32> — do not implement Copy. When you assign one to another variable, the value is moved: the original binding becomes invalid.
fn main() { let s1 = String::from("hello"); let s2 = s1; // s1 is moved into s2
// println!("{}", s1); // ERROR: s1 was moved println!("{}", s2); // fine}The same rule applies when you pass a value into a function:
fn consume(s: String) { println!("{}", s);} // s is dropped here
fn main() { let name = String::from("Rust"); consume(name); // name is moved // name is no longer valid here}Copy Types Are Duplicated
Section titled “Copy Types Are Duplicated”Simple scalar types stored entirely on the stack — integers, bool, char, floating-point numbers, and tuples of these — implement Copy. Assigning or passing them makes a bitwise copy; the original remains valid.
fn main() { let x: i32 = 42; let y = x; // x is COPIED, not moved println!("x={} y={}", x, y); // both valid}Common Copy types: i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64, bool, char.
Explicit Deep Copy with .clone()
Section titled “Explicit Deep Copy with .clone()”When you need a full independent copy of a heap-owning value, call .clone(). This is an explicit opt-in to a potentially expensive operation.
fn main() { let original = String::from("keep me"); let copy = original.clone(); // deep copy println!("original={} copy={}", original, copy);}Returning Ownership
Section titled “Returning Ownership”A function can give ownership back to the caller by returning the value:
fn take_and_give_back(s: String) -> String { s // ownership moves out of the function}
fn main() { let s1 = String::from("hello"); let s2 = take_and_give_back(s1); println!("{}", s2);}This is valid but verbose. In practice, you will almost always prefer borrowing (the next lesson) over moving-and-returning.
fn take_ownership(s: String) -> String { println!("inside take_ownership: {}", s); s // return ownership to caller}
fn main() { // String is NOT Copy — it moves let s1 = String::from("hello"); let s2 = take_ownership(s1); // s1 is moved println!("s2 now owns: {}", s2);
// i32 IS Copy — it is copied let x = 42; let y = x; println!("x={} y={}", x, y);
// .clone() makes an explicit deep copy let original = String::from("keep me"); let cloned = original.clone(); println!("original={} cloned={}", original, cloned);}Compiling…