Can you write this from memory?
Declare a String variable `s1` with value "hello", then move it to `s2`.
Ownership is Rust's most distinctive feature, and the one that trips up most newcomers. The rules are simple: each value has one owner, values are dropped when the owner goes out of scope, and you can borrow values without taking ownership.
Simple in theory. In practice, you're staring at a borrow checker error wondering: Why can't I use this value? Did I move it? Should this be & or &mut?
This page focuses on ownership patterns until the rules become intuition. From move semantics to borrowing to the critical difference between shared and mutable references.
The most common ownership error. You used a value after ownership was transferred:
// WRONG: using a value after move
let s1 = String::from("hello");
let s2 = s1;
// println!("{s1}"); // error[E0382]: borrow of moved value
// RIGHT Option 1: borrow instead of move
let s1 = String::from("hello");
let s2 = &s1;
println!("{s1}"); // OK: s1 was only borrowed
println!("{s2}");
// RIGHT Option 2: clone if you need independent ownership
let s1 = String::from("hello");
let s2 = s1.clone();
println!("{s1} {s2}"); // both valid
Moves also happen when passing to functions. If you only need to read, take &T instead of T:
// WRONG: takes ownership unnecessarily
// fn print_len(s: String) { println!("{}", s.len()); }
// RIGHT: borrows — caller keeps ownership
fn print_len(s: &str) { println!("{}", s.len()); }
An active & reference exists while you try to take &mut:
// WRONG: mutable borrow while immutable borrow is active
let mut v = vec![1, 2, 3];
let first = &v[0];
// v.push(4); // error[E0502]: cannot borrow v as mutable
// println!("{first}");
// RIGHT: end the immutable borrow before mutating
let mut v = vec![1, 2, 3];
let first = v[0]; // Copy the value instead of borrowing
v.push(4); // no conflict, no active borrow
println!("{first}");
The rule: at any point, you can have either one &mut T or any number of &T, never both simultaneously.
Something still holds a reference, so ownership cannot transfer:
// WRONG: moving while borrowed
let s = String::from("hello");
let r = &s;
// let s2 = s; // error[E0505]: cannot move out of s because it is borrowed
// println!("{r}");
// RIGHT: scope the borrow before moving
let s = String::from("hello");
{
let r = &s;
println!("{r}");
} // r's borrow ends here
let s2 = s; // move is fine now
A function creates a value and tries to return a reference to it. The value is dropped when the function returns, leaving a dangling pointer:
// WRONG: returning a reference to a local
// fn make_greeting() -> &str {
// let s = String::from("hello");
// &s // error[E0515]: s is dropped here
// }
// RIGHT: return the owned value
fn make_greeting() -> String {
String::from("hello")
}
If the function creates the data, it must return the owned value. References only work for data that outlives the function call (e.g., passed in by the caller). When references do need explicit lifetime annotations, the references and lifetimes guide covers the syntax.
A common source of confusion: borrows don't last until the end of the block. Since Rust 2018, the compiler ends a borrow at its last use, not at the closing }:
let mut v = vec![1, 2, 3];
let first = &v[0];
println!("{first}"); // last use of first — borrow ends here
v.push(4); // OK! no active borrow
This is called non-lexical lifetimes (NLL). If you get E0502, check whether you can move the last use of the immutable reference above the mutation.
| Behavior | Types | What happens on assignment |
|---|---|---|
| Copy | Integers, bools, char, f32/f64, tuples/arrays of Copy types | Implicit bitwise duplication — original stays valid |
| Move | String, Vec, Box, any type without Copy | Ownership transfers — original is invalid |
| Clone | Any type implementing Clone | Explicit duplication via .clone() — may or may not be expensive |
let a = 5;
let b = a; // Copy: a is still valid
println!("{a}");
let s1 = String::from("hi");
let s2 = s1; // Move: s1 is now invalid
let s3 = s2.clone(); // Clone: s2 is still valid
println!("{s2} {s3}");
Clone calls arbitrary code — for String it allocates a new heap buffer, but for Rc it just increments a counter. "Clone = deep copy" is not always true.
- Use
&Tto read without taking ownership. - Use
&mut Tto mutate without taking ownership. - Prefer
&strover&String— it accepts bothStringand string literals.
// Prefer &str for read-only string access
fn word_count(s: &str) -> usize {
s.split_whitespace().count()
}
fn push_excl(s: &mut String) {
s.push('!');
}
let owned = String::from("hello world");
word_count(&owned); // &String coerces to &str
word_count("literal"); // &str directly
Slices (&str, &[T]) are references to a contiguous sequence within owned data:
let s = String::from("hello world");
let first_word: &str = &s[..5]; // "hello"
println!("{first_word}");
let nums = [1, 2, 3, 4, 5];
let middle: &[i32] = &nums[1..4]; // [2, 3, 4]
println!("{middle:?}");
Slices borrow the original data, so the same borrow rules apply — you cannot mutate the owned value while a slice is active. The Rust strings guide dives deeper into &str slices specifically.
You cannot move a field out of a borrowed struct. Use std::mem::take or std::mem::replace to swap in a default:
use std::mem;
struct Config {
name: String,
retries: u32,
}
let mut config = Config { name: "prod".into(), retries: 3 };
// Take name out, leaving String::default() ("") in its place
let name = mem::take(&mut config.name);
println!("{name}"); // "prod"
println!("{}", config.name); // ""
This is common in builders, state machines, and any pattern where you need to extract an owned value from a mutable reference. For a printable quick-reference of these patterns, see the ownership and borrowing cheat sheet.
- The Rust Book: Ownership — move semantics, scope, drop
- The Rust Book: References and Borrowing — & vs &mut rules
- The Rust Book: The Slice Type — &str and &[T]
- Rust Blog: Non-Lexical Lifetimes — borrows end at last use
When to Use Rust Ownership & Borrowing: Move Semantics & Borrow Rules
- Take ownership when you need to store or return a value.
- Borrow with `&` when you only need to read.
- Borrow with `&mut` when you need to modify without taking ownership.
- Use `Clone` when you need a copy and the type supports it.
- Use `Copy` types (integers, bools, etc.) when you want implicit copying.
Check Your Understanding: Rust Ownership & Borrowing: Move Semantics & Borrow Rules
Explain the borrowing rules in Rust.
At any time, you can have either one mutable reference OR any number of immutable references (not both). References must always be valid, so no dangling pointers. This prevents data races at compile time.
What You'll Practice: Rust Ownership & Borrowing: Move Semantics & Borrow Rules
Common Rust Ownership & Borrowing: Move Semantics & Borrow Rules Pitfalls
- Symptom: `error[E0382]: borrow of moved value`. Why: You assigned or passed an owned value, then tried to use the original. Fix: Borrow with `&` instead of moving, or call `.clone()` if you need both.
- Symptom: `error[E0502]: cannot borrow as mutable because it is also borrowed as immutable`. Why: An active `&` reference exists and you tried to take `&mut`. Fix: End the immutable borrow (let the variable go out of scope or stop using it) before mutating.
- Symptom: `error[E0515]: cannot return reference to local variable`. Why: The local is dropped at the end of the function, so the reference would dangle. Fix: Return the owned value instead of a reference.
- Symptom: `error[E0505]: cannot move out of value because it is borrowed`. Why: Something still holds a `&` or `&mut` to the value. Fix: Scope the borrow so it ends before the move, or clone.
- Symptom: Littering `.clone()` everywhere to silence the compiler. Why: Clone works but hides the real issue — your ownership flow is wrong. Fix: Restructure to borrow where possible; clone only when you genuinely need independent ownership.
- Symptom: Confusion between `let mut x` and `&mut x`. Why: `mut` on a binding means you can reassign it; `&mut` means you have exclusive mutable access to borrowed data. Fix: Treat them as independent concepts — you can have `let mut x = &val` (rebindable shared ref) or `let x = &mut val` (non-rebindable exclusive ref).
Rust Ownership & Borrowing: Move Semantics & Borrow Rules FAQ
Why does assignment move the value?
For types without `Copy`, assignment transfers ownership to prevent double-free bugs. The original variable becomes invalid. This is called a "move".
What types implement Copy?
Simple scalar types: integers, floats, bools, chars. Also tuples and arrays of Copy types. Types that manage heap memory (String, Vec) do not implement Copy, so they must be cloned explicitly.
Can I have multiple mutable references?
No, not to the same data at the same time. You can have one `&mut T` OR any number of `&T`, never both. This prevents data races.
What does "value moved here" mean?
You tried to use a value after ownership was transferred elsewhere (via assignment, function call, etc.). Either borrow instead of moving, or clone the value.
When should I use clone vs borrow?
Borrow when possible since it's zero-cost. Clone when you genuinely need independent ownership. Avoid cloning just to silence the compiler — it usually means the ownership flow needs restructuring.
Don't borrows last until the end of the block?
Not since Rust 2018. Non-lexical lifetimes (NLL) mean a borrow ends at its last use, not at the closing `}`. This is why moving a `println!` above a mutation can fix E0502.
How do I return a reference from a function?
The referenced data must outlive the function. You can return references to data passed in by the caller (with lifetime annotations), but you cannot return a reference to a local variable — return the owned value instead.
Rust Ownership & Borrowing: Move Semantics & Borrow Rules Syntax Quick Reference
let s1 = String::from("hello");
let s2 = s1;
println!("{s2}");let s1 = String::from("hello");
let s2 = s1.clone();
println!("{s1} {s2}");let s = String::from("hello");
let len = s.len();
println!("{s} is {len} bytes");let mut s = String::from("hello");
let r = &mut s;
r.push_str(", world");fn takes_ownership(s: String) {
println!("{s}");
}fn word_count(s: &str) -> usize {
s.split_whitespace().count()
}fn change(s: &mut String) {
s.push_str(", world");
}let s = String::from("hello");
let r1 = &s;
let r2 = &s;
println!("{r1} {r2}");let s = String::from("hello");
let slice: &str = &s[0..2];
println!("{slice}");Rust Ownership & Borrowing: Move Semantics & Borrow Rules Sample Exercises
Fill in the assignment to show that `x` is still usable after the copy to `y`.
xFill in the code to create a deep copy of s1 for s2.
s1.clone()Fill in the function call that moves ownership of `name`.
consume+ 36 more exercises
Copy-ready syntax examples for quick lookup