Ownership is Rust's most unique feature and it's what allows Rust to make memory safety guarantees without needing a garbage collector.
This documentation is based on insights shared by Obot (@oboobotenefiok).
- Each value in Rust has a variable that’s called its owner.
- There can only be one owner at a time.
- When the owner goes out of scope, the value will be dropped (freed from memory).
In many languages, assigning one variable to another creates a shallow copy or a reference. In Rust, for types that store data on the heap (like String), assigning one variable to another moves the ownership.
When you move a value, the original variable is no longer valid.
fn main() {
let indie_hacker = String::from("Caleb"); // indie_hacker owns the string
let underdog_builder = indie_hacker; // OWNERSHIP MOVES to underdog_builder
// println!("{}", indie_hacker);
// ^ ERROR! indie_hacker no longer owns the value.
println!("New owner is: {}", underdog_builder);
}Tip
To "fix" a move, you can use .clone(). This creates a deep copy of the data on the heap, giving you a second independent owner. This is used when you actually need two copies of the data, but be careful—cloning heap data can be expensive!
Note
This prevents "double free" errors. If both variables were valid without cloning, they would both try to free the same memory when they go out of scope.
Variables are only accessible within the block {} they are defined in. Once the block ends, the variable is "dropped".
If a variable is defined inside a function, it is local to that function.
fn status() {
let indie_hacker = String::from("Caleb"); // local to status()
// indie_hacker exists here
}
// indie_hacker is DROPPED here. It cannot be accessed outside.The lifetime of a variable is the duration for which it exists in memory. Once the function status() ends, the memory for indie_hacker is automatically freed.
Rust behaves differently depending on where data is stored:
- Heap (
String::from("...")): These are moved because they are dynamically sized and stored on the heap. Copying them would be expensive. - Stack (String Literals
"..."): Simple values with a known size at compile time (like integers or basic string literals) are stored on the stack. These are copied instead of moved because copying them is very fast.
let x = "Caleb"; // Stack literal
let y = x; // Copied, not moved. Both are valid.
println!("{} and {}", x, y); // Works fine!Check the examples/ownership/ folder for runnable code snippets:
- move_error.rs: [Intentional Error] See what happens when you use a moved variable.
- move_fixed.rs: [Fixed] How to use
.clone()to keep both variables valid. - scope_dropped.rs: Visualizing when variables are freed from memory.
- stack_copy.rs: Understanding why simple types don't "move."
Credit: Based on explanations by @oboobotenefiok (@oboobotenefiok)