Rust Ownership and Borrowing
debt(d1/e2/b5/t7)
Closest to 'caught instantly' (d1), rustc itself blocks compilation with errors like 'borrow of moved value' and 'cannot borrow as mutable'; the borrow checker is the compiler so misuse never reaches runtime.
Closest to 'simple parameterised fix' (e3), quick_fix is replacing a move with &value or &mut value — usually a one-to-few-line change, occasionally requiring small signature adjustments, so slightly above a pure one-line swap.
Closest to 'persistent productivity tax' (b5), ownership applies across all contexts (web/cli/queue/library) and shapes how data flows through every function signature and lifetime, slowing many work streams until internalized.
Closest to 'serious trap' (t7), the misconception is that assignment copies as in most languages, but non-Copy types are moved and invalidate the original — directly contradicting how nearly every other mainstream language behaves.
Also Known As
TL;DR
Explanation
Ownership is the core rule that makes Rust memory-safe without a garbage collector. Every value has exactly one owning variable, and when that owner goes out of scope the value is dropped and its resources freed. There is no double-free and no use-after-free because the compiler tracks who owns what and when it ends. Assigning or passing a non-Copy value by value moves ownership: the original binding becomes invalid, so two variables can never both believe they own the same heap allocation.
Moves are why `let b = a;` for a `String` leaves `a` unusable - the heap buffer now belongs to `b`. Small types that implement `Copy` (integers, booleans, char, and tuples of Copy types) are bit-copied instead of moved, so the original stays valid. Understanding this split is the first hurdle: trying to use a moved value produces the classic 'value borrowed here after move' error.
Borrowing lets you access a value without taking ownership by passing a reference. There are two kinds. A shared reference `&T` grants read-only access and you may have many of them at once. A mutable reference `&mut T` grants read-write access but is exclusive: while it exists, no other reference of any kind to that value may exist. This is the borrow rule - any number of readers, or exactly one writer, never both. It is enforced at compile time and eliminates data races by construction.
The borrow checker also ensures no reference outlives the data it points to, which connects ownership to lifetimes. A reference is always tied to the scope of its owner, so you cannot return a reference to a local that is about to be dropped.
Newcomers fight the borrow checker by cloning everything to dodge errors, which works but discards Rust's performance advantage and signals a model not yet internalized. The idiomatic path is to borrow where you only need to read, take `&mut` where you need to mutate in place, and move ownership only when the callee genuinely needs to keep the value. Once the rules click, ownership stops being a fight and becomes a precise way to express who is responsible for each resource.
Common Misconception
Why It Matters
Common Mistakes
- Using a value after it was moved into another variable or passed by value to a function.
- Cloning everywhere to silence borrow errors instead of passing a reference where only read access is needed.
- Holding a shared reference while trying to take a mutable reference to the same value, violating the exclusivity rule.
- Returning a reference to a local variable that is dropped at the end of the function.
- Forgetting that &mut is exclusive, so iterating over a collection while mutating it fails to compile.
Avoid When
- A function genuinely needs to take ownership and store the value, where moving by value is correct rather than borrowing.
- The value is a small Copy type like an integer, where copying is cheaper and clearer than threading a reference.
- Reaching for shared mutability across threads, where Arc and Mutex or interior mutability are the right tools instead of plain references.
When To Use
- Passing a value to a function that only reads it, where a shared reference avoids an unnecessary move or clone.
- Mutating a collection or struct in place via an exclusive mutable reference instead of returning a new copy.
- Designing APIs that make ownership transfer explicit so callers know who is responsible for a resource.
- Avoiding clones in hot paths by borrowing data that lives long enough to satisfy the borrow checker.
Code Examples
fn print_len(s: String) {
println!("length is {}", s.len());
}
fn main() {
let name = String::from("alice");
// Moves name into print_len; name is invalid afterward.
print_len(name);
// Error: value borrowed here after move.
println!("name was {}", name);
let mut total = vec![1, 2, 3];
let first = &total[0]; // shared borrow
// Error: cannot borrow total as mutable while a shared borrow is live.
total.push(4);
println!("{}", first);
}
// Borrow instead of taking ownership so the caller keeps the value.
fn print_len(s: &String) {
println!("length is {}", s.len());
}
fn main() {
let name = String::from("alice");
print_len(&name);
// name is still valid because we only borrowed it.
println!("name was {}", name);
let mut total = vec![1, 2, 3];
// Finish reading before mutating: copy the small value out.
let first = total[0];
total.push(4);
println!("first was {}, vec is now {:?}", first, total);
}