juniorRust

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:

main.rs
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
main.rs
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.
Rust
let mut name = String::from("Rust");
let r = &mut name;
r.push_str(" Lang");
// let r2 = &mut name; // ❌ Not allowed while r is active

These 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

Rendering diagram…

Ownership vs Borrowing

ConceptOwnershipBorrowing
Transfers control?Yes (move)No
Original variable usable afterward?No (unless Copy)Yes
Uses references?NoYes (&T, &mut T)
Primary purposeManage lifetimeTemporary 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.

Rust
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?

More Rust interview questions

View all →