TL;DR
- Rust uses an ownership model to manage memory safely without needing a garbage collector.
- Each value has one owner — when that owner goes out of scope, the value is automatically dropped.
- Ownership can be transferred when assigning variables or passing to functions.
- Borrowing lets you use data without taking ownership, using references (
&T
or&mut T
). - Rust enforces strict rules to prevent data races and dangling references, all checked at compile time.
Rust is a systems programming language that guarantees memory safety without requiring a garbage collector. At the heart of this guarantee are two key concepts: rust ownership and borrowing. These features make Rust unique among programming languages, enabling it to prevent common bugs like null pointer dereferences, dangling pointers, and data races at compile time.

What is Rust Ownership?
Ownership is Rust’s most distinctive feature. It’s a set of rules that govern how memory is managed in Rust programs. The ownership system ensures that memory is automatically freed when a variable goes out of scope, while also preventing common memory-related bugs.
The Three Rules of Ownership
- Each value in Rust has an owner – There’s always exactly one variable that owns a value.
- There can only be one owner at a time – When you assign a value to another variable or pass it to a function, ownership is transferred.
- When the owner goes out of scope, the value is dropped – Rust automatically calls the
drop
function to clean up the memory.
fn main() {
let s1 = String::from("hello"); // s1 owns the String
let s2 = s1; // Ownership moves to s2
// println!("{}", s1); // This would error - s1 no longer owns the String
println!("{}", s2); // This works fine
} // s2 goes out of scope and the String is dropped
What is Rust Borrowing?
While ownership ensures memory safety, transferring ownership every time you want to use a value would be cumbersome. This is where borrowing comes in – it allows you to temporarily access a value without taking ownership of it.
The Two Types of Borrowing
- Immutable borrows (
&T
) – Allow reading but not modifying the borrowed value. - Mutable borrows (
&mut T
) – Allow modifying the borrowed value.
fn main() {
let s = String::from("hello");
// Immutable borrow
let len = calculate_length(&s);
println!("The length of '{}' is {}.", s, len);
// Mutable borrow
let mut s = String::from("hello");
change_string(&mut s);
println!("{}", s);
}
fn calculate_length(s: &String) -> usize {
s.len()
}
fn change_string(s: &mut String) {
s.push_str(", world!");
}
The Borrowing Rules
Rust enforces strict rules around borrowing to prevent data races and other memory issues:
- You can have either one mutable reference or any number of immutable references to a particular piece of data in a particular scope.
- References must always be valid – You can’t have dangling references.
These rules are checked at compile time, meaning many potential bugs are caught before your code even runs.
fn main() {
let mut s = String::from("hello");
let r1 = &s; // No problem
let r2 = &s; // No problem
// let r3 = &mut s; // BIG PROBLEM - can't have mutable borrow while immutable borrows exist
println!("{} and {}", r1, r2);
// r1 and r2 are no longer used after this point
let r3 = &mut s; // Now this is OK
r3.push_str(", world!");
}
Why Ownership and Borrowing Matter
- Memory Safety – No dangling pointers, no null pointer dereferences.
- No Data Races – Rust’s borrowing rules prevent multiple threads from unsafely accessing the same data.
- No Garbage Collection Overhead – Memory is managed deterministically at compile time.
- Explicit Control – You know exactly when memory is allocated and freed.

Common Ownership Patterns
1. Returning Ownership
fn give_ownership() -> String {
let s = String::from("hello");
s // Ownership is returned
}
2. Taking and Giving Back Ownership
fn take_and_give_back(s: String) -> String {
s // Ownership is returned
}
3. Using References Instead of Ownership
fn calculate_length(s: &String) -> usize {
s.len()
} // s goes out of scope but since it's a reference, nothing is dropped
Lifetimes
While not covered in detail here, lifetimes are another important concept that works with ownership and borrowing. They ensure that references are valid for as long as they’re needed.
Conclusion
Rust ownership and borrowing are fundamental to Rust’s approach to memory safety. While these concepts might seem restrictive at first, they enable Rust programs to be both safe and efficient. The compiler’s strict checks mean you’ll catch many potential bugs early in the development process. As you become more familiar with these concepts, you’ll find they lead to more robust and maintainable code.
Rust’s ownership model represents a significant shift from how most other languages handle memory management, but it’s this very model that gives Rust its unique advantages in performance and safety.
FAQs
What happens when ownership is moved?
- When ownership is moved, the original variable can no longer be used. This prevents multiple variables from trying to free the same memory.
Can I clone data to keep using the original variable?
- Yes, using the
.clone()
method creates a deep copy of the data, allowing both variables to own their respective copies.
Why does Rust restrict multiple mutable references?
- To prevent data races, Rust enforces that only one mutable reference to a value exists at a time. This ensures safe concurrent access.
How does borrowing help with memory safety?
- Borrowing allows functions to access data without taking ownership, ensuring that the data remains valid and preventing dangling references.