Ownership / Move
| Since: | Rust 1.0(2015) |
|---|
Rust's ownership system enforces a unique rule: every value has exactly one owner. This guarantees memory safety at compile time. When the owner goes out of scope, the value is automatically freed.
Syntax
let variable1 = value;
let variable2 = variable1; // Ownership of variable1 moves to variable2 (variable1 becomes invalid).
// Moving ownership into a function
fn function_name(arg: Type) { ... }
function_name(variable); // Ownership of variable moves into the function.
// Explicitly free memory
drop(variable);
Overview
| Rule | Description |
|---|---|
| Single owner | Each value has exactly one owning variable at any given time. |
| Freed at scope end | When the owner goes out of scope, the value's memory is automatically freed. |
| Move semantics | Assignment or passing to a function moves ownership, making the original variable unusable. |
| drop() | Used to immediately free a variable that holds ownership. |
Sample Code
sample_ownership_move.rs
fn take_ownership(s: String) {
println!("{}", s);
} // s is freed here.
fn main() {
let s1 = String::from("hello");
let s2 = s1; // Ownership of s1 moves to s2.
// println!("{}", s1); // Error: s1 is no longer valid.
println!("{}", s2); // Prints "hello".
let s3 = String::from("world");
take_ownership(s3); // Ownership of s3 moves into the function.
// println!("{}", s3); // Error: s3 is no longer valid.
// Immediately free s4 with drop().
let s4 = String::from("drop me");
drop(s4);
// println!("{}", s4); // Error: s4 has already been freed.
// Types that implement the Copy trait are copied instead of moved.
let x = 5;
let y = x; // x is still valid.
println!("x={}, y={}", x, y); // Both are usable.
}
Compile with the following command:
rustc ownership_move.rs ./ownership_move hello world x=5, y=5
Advanced Patterns
sample_ownership_advanced.rs
fn create_name() -> String {
String::from("user_a") // ownership is returned to the caller
}
fn process(name: String) -> String {
format!("name: {}", name) // takes and returns ownership
}
// Pattern 2: Copy types are duplicated automatically
fn show_i32(n: i32) {
println!("{}", n);
}
// Pattern 3: Clone copies heap data explicitly
fn show_string(s: &String) {
println!("{}", s);
}
use std::thread;
fn main() {
// Returning ownership
let name = create_name();
let result = process(name); // ownership of `name` moves into process()
println!("{}", result);
// `name` cannot be used here
// Copy: i32 is Copy, so no move occurs
let x = 42i32;
show_i32(x);
println!("x is still usable: {}", x); // OK
// Clone: explicitly copy a String
let s = String::from("user_c");
let s_clone = s.clone();
show_string(&s);
show_string(&s_clone);
println!("both usable: {} / {}", s, s_clone);
// move closure: the closure takes ownership of captured variables
let message = String::from("hello");
let handle = thread::spawn(move || {
println!("from thread: {}", message); // ownership moved here
});
// println!("{}", message); // compile error: moved
handle.join().unwrap();
}
Compile with the following command:
rustc sample_ownership_advanced.rs ./sample_ownership_advanced name: user_a x is still usable: 42 user_c user_c both usable: user_c / user_c from thread: hello
Common Mistakes
Common mistake 1: using a variable after its ownership has moved
Passing a value to a function or assigning it to another variable transfers ownership. The original variable cannot be used after the move.
fn take_ownership(s: String) {
println!("{}", s);
}
fn main() {
let s = String::from("user_e");
take_ownership(s); // ownership of `s` is moved
// Compile error: `s` has been moved
// println!("{}", s);
}
The same logic can also be written as:
fn take_ownership(s: String) {
println!("{}", s);
}
fn main() {
let s = String::from("user_e");
// Option 1: pass a reference — ownership stays with the caller
println!("{}", &s);
take_ownership(s.clone()); // clone a copy to pass
println!("{}", s); // s still usable
}
Common mistake 2: confusing Copy and Clone
Types that implement Copy (like i32, bool, f64) are duplicated implicitly on assignment. Types like String and Vec do not implement Copy and must be cloned explicitly.
fn main() {
// i32 is Copy: assignment duplicates the value
let x: i32 = 10;
let y = x;
println!("{} {}", x, y); // both usable
// String is not Copy: assignment moves ownership
let s1 = String::from("hello");
let s2 = s1;
// println!("{}", s1); // compile error: s1 was moved
// Clone explicitly copies the heap data
let s3 = String::from("world");
let s4 = s3.clone();
println!("{} {}", s3, s4); // both usable
}
Details
Rust's ownership system guarantees memory safety without a garbage collector. When a value goes out of scope, drop() is called automatically and the memory is freed (the RAII pattern). RAII (Resource Acquisition Is Initialization) is a pattern that ties resource acquisition and release to a variable's lifecycle.
Using a variable after its ownership has been moved causes a compile error. If you need to use a value in multiple places, use borrowing (& references) or clone().
Types that implement the Copy trait — such as integers, floating-point numbers, booleans, and chars — are copied on assignment, so the original variable remains valid.
If you find any errors or copyright issues, please contact us.