What are ownership, borrowing, and references in Rust?
Updated Feb 20, 2026
Short answer
Ownership is Rust's memory management model that guarantees memory safety without a garbage collector. Borrowing lets code temporarily access a value without taking ownership, while references (&T and &mut T) are the mechanism used to borrow values safely. Together, these rules prevent common bugs like use-after-free, data races, and double frees at compile time.
Deep explanation
Rust's memory safety comes from a simple but powerful idea: every value has exactly one owner. When that owner goes out of scope, Rust automatically frees the value. This means you do not manually call free(), and Rust does not need a garbage collector.
A value can have only one owner at a time, and it is dropped automatically when its owner goes out of scope.
Ownership
Ownership follows three core rules:
- Every value has one owner.
- There can only be one owner at a time.
- When the owner leaves scope, the value is automatically dropped.
For example:
fn main() { let s = String::from("Rust");
let t = s; // Ownership moves from s to t
println!("{}", t); // println!("{}", s); // ❌ Compile error: s no longer owns the String}Unlike primitive types such as i32, heap-allocated types like String are moved by default instead of copied. This prevents two variables from trying to free the same memory.
Borrowing with References
Sometimes you only need to use a value rather than own it. Rust allows this through borrowing, using references.
&T→ immutable reference&mut T→ mutable reference
fn print_length(text: &String) { println!("{}", text.len());}
fn main() { let s = String::from("hello");
print_length(&s);
println!("{}", s); // Still valid because ownership never moved}The function borrows the String, so the caller keeps ownership.
Borrowing allows functions to access data without taking ownership, avoiding unnecessary copies.
Mutable Borrowing Rules
Rust prevents data races by restricting mutable access.
- You may have many immutable references, or
- One mutable reference, but not both at the same time.
let mut name = String::from("Rust");
let r = &mut name;r.push_str(" Lang");
// let r2 = &mut name; // ❌ Not allowed while r is activeThese rules let Rust guarantee exclusive access when mutation happens.
💡 Rule of thumb: Read with many immutable references, write with one mutable reference.
Lifetimes and Safety
References must never outlive the data they point to. The compiler checks this automatically using lifetimes.
You rarely need to write lifetime annotations as a beginner, but the compiler still verifies that every borrowed reference remains valid.
References are only valid while the original value still exists.
Ownership Flow
Ownership vs Borrowing
| Concept | Ownership | Borrowing |
|---|---|---|
| Transfers control? | Yes (move) | No |
| Original variable usable afterward? | No (unless Copy) | Yes |
| Uses references? | No | Yes (&T, &mut T) |
| Primary purpose | Manage lifetime | Temporary access |
Real-world example
Imagine a web server receives a request containing a large JSON payload stored in a String. Multiple validation functions need to inspect the payload, but none should own or duplicate it. Passing immutable references avoids copying potentially large amounts of data while keeping ownership with the request handler.
fn is_valid(payload: &String) -> bool { payload.contains("user")}
fn log_request(payload: &String) { println!("Received {} bytes", payload.len());}
fn main() { let request = String::from(r#"{"user":"alice"}"#);
log_request(&request); println!("{}", is_valid(&request));}Passing references is typically faster than cloning large data structures because no new allocation is needed.
Common mistakes
- * **Using a moved value** — Attempting to use a variable after its ownership has been moved. Borrow it with `&` or clone it if you truly need another owned copy.
- * **Multiple mutable borrows** — Creating two active `&mut` references at once violates Rust's aliasing rules and fails to compile.
- * **Mixing mutable and immutable borrows** — Reading and writing through different references simultaneously is not allowed because it could lead to inconsistent state.
- * **Cloning unnecessarily** — Calling `clone()` to silence ownership errors often hides a design issue. Prefer borrowing when ownership does not actually need to change.
- * **Returning invalid references** — Never return a reference to data that will be dropped before the caller can use it.
Follow-up questions
- What is the difference between moving and copying in Rust?
- When should you use &T versus &mut T?
- Why doesn't Rust need a garbage collector?
- What problem do lifetimes solve?