π― References and Borrowing in Rust
The Library Book Analogy π
Imagine you have a super special library book. In Rust, variables are like owning that book. But what if your friend wants to read it too? You have two choices:
- Give them the book (they own it now, you canβt use it!)
- Let them borrow it (they can read, but you still own it!)
This is exactly what References and Borrowing is all about!
π What is a Reference?
A reference is like giving someone your library card number instead of the actual book. They can look at the book, but you still own it!
fn main() {
let book = String::from("Harry Potter");
// Create a reference with &
let book_ref = &book;
println!("I still have: {}", book);
println!("Friend sees: {}", book_ref);
}
π Key Point
- The
&symbol creates a reference - The original variable (
book) keeps ownership - The reference (
book_ref) can read the data
graph TD A["book owns the data"] --> B["&book = reference"] B --> C["book_ref points to book"] C --> D["Both can read!"]
π€ What is Borrowing?
Borrowing is the act of creating a reference. When a function βborrowsβ data, it uses a reference instead of taking ownership.
Without Borrowing (Ownership Moves) β
fn print_book(s: String) {
println!("{}", s);
}
fn main() {
let book = String::from("Rust Book");
print_book(book);
// ERROR! book moved away!
// println!("{}", book);
}
With Borrowing (Reference) β
fn print_book(s: &String) {
println!("{}", s);
}
fn main() {
let book = String::from("Rust Book");
print_book(&book);
// Works! We still own book!
println!("{}", book);
}
π¨ Think of it like this:
| Action | Real Life | Rust |
|---|---|---|
| Give book | Friend takes it | fn foo(s: String) |
| Lend book | Friend borrows | fn foo(s: &String) |
βοΈ Mutable References
What if your friend wants to write notes in your book? You need to give them special permission!
A mutable reference lets the borrower change the data.
fn main() {
// Must be 'mut' to allow changes
let mut book = String::from("Chapter 1");
// Mutable reference with &mut
add_chapter(&mut book);
println!("{}", book);
// Prints: Chapter 1, Chapter 2
}
fn add_chapter(s: &mut String) {
s.push_str(", Chapter 2");
}
π Two Types of References
| Type | Symbol | Can Read? | Can Write? |
|---|---|---|---|
| Immutable | & |
β Yes | β No |
| Mutable | &mut |
β Yes | β Yes |
graph TD A["Reference Types"] --> B["&T = Read Only"] A --> C["&mut T = Read + Write"] B --> D["Many can exist at once"] C --> E["Only ONE at a time!"]
π The Reference Rules
Rust has very important rules to keep your data safe. Think of them like library rules!
Rule 1: One Writer OR Many Readers
At any time, you can have:
- β
Many immutable references (
&T), OR - β
One mutable reference (
&mut T) - β Never both at the same time!
fn main() {
let mut data = String::from("hello");
// Many readers? OK!
let r1 = &data;
let r2 = &data;
println!("{} {}", r1, r2);
// One writer? OK!
let w1 = &mut data;
w1.push_str(" world");
}
Why This Rule Exists π€
Imagine two friends writing in your book at the same timeβchaos! Rust prevents this at compile time.
// This will NOT compile!
fn main() {
let mut s = String::from("hi");
let r1 = &s; // Reader 1
let w1 = &mut s; // Writer - ERROR!
println!("{}", r1);
}
Rule 2: References Must Be Valid
A reference must always point to valid data. Rust checks this for you!
β οΈ Dangling References
A dangling reference is like a library card pointing to a book thatβs been thrown away. It points to nothing!
The Problem
// This will NOT compile!
fn dangle() -> &String {
let s = String::from("hello");
&s // s is destroyed here!
} // Reference points to nothing!
π‘οΈ Rust Saves You!
Rust refuses to compile dangling references. Youβll never have a crash from this bug!
The Fix
Instead of returning a reference, return the owned value:
fn no_dangle() -> String {
let s = String::from("hello");
s // Return ownership, not reference!
}
graph TD A["Function creates String"] --> B["Function ends"] B --> C{What to return?} C --> D["&String = DANGER!<br/>Data is gone!"] C --> E["String = SAFE!<br/>Ownership moves out"] D --> F["β Compile Error"] E --> G["β Works!"]
π― Quick Summary
| Concept | What It Means | Symbol |
|---|---|---|
| Reference | Borrow without owning | & |
| Borrowing | Using a reference | &variable |
| Mutable Ref | Borrow + can change | &mut |
| Rules | 1 writer OR many readers | - |
| Dangling | Invalid reference | Compiler stops it! |
π Why This Matters
With these rules, Rust guarantees:
- β No data races
- β No use-after-free bugs
- β No null pointer crashes
- β Memory safety without garbage collection!
Youβre now a borrowing expert! π
The compiler is your friendβit catches these bugs before your code ever runs!
