References & Borrowing
Rust is a systems programming language focused on speed, memory safety, and fearless concurrency — and borrowing lets you use a value without taking ownership of it, which makes ownership practical day to day.
Learn References & Borrowing in our free Rust course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Rust course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
In this lesson you'll create shared references with & , mutable references with &mut , and learn the borrow checker's rules that keep this all safe.
What You'll Learn in This Lesson
1️⃣ Shared References with &
A reference lets a function access a value without owning it. You create one with the & operator and accept one with a & type. Because the function only borrows, the original variable stays valid after the call — no move, no clone needed.
Compare this with the previous lesson: there, passing a String into a function moved it away. Here &s lends it, so s is still usable on the next line.
2️⃣ Mutable References and the Borrow Rules
To let a borrower change a value, use a mutable reference &mut . The borrow checker enforces a strict rule: at any moment you can have either any number of shared & references or exactly one &mut reference — never both. This is what prevents data races at compile time.
Notice the ordering: the two shared borrows r1 and r2 are used and finished before the mutable borrow r3 begins. Overlapping a shared and a mutable borrow would be a compile error.
Your turn. Fill in the blanks marked ___ , then run it.
Write two functions — one that borrows immutably, one that borrows mutably — and use both from main . Run it with cargo run .
📋 Quick Reference — Borrowing
Practice quiz
What does the & operator create when you write f(&s)?
- A copy of s
- A move of s into f
- A reference that borrows s without taking ownership
- A mutable clone of s
Answer: A reference that borrows s without taking ownership. & creates a reference: the function borrows the value, so the original stays valid after the call.
After let len = calculate_length(&s); is s still usable on the next line?
- Yes, because it was only borrowed
- No, it was moved
- Only if s is mut
- Only if you clone it first
Answer: Yes, because it was only borrowed. Borrowing with & does not move ownership, so s remains valid after the call.
What is the borrow rule the compiler enforces?
- Any number of &mut at once
- Exactly one & and one &mut together
- References are never allowed
- Either many shared & OR exactly one &mut, never both
Answer: Either many shared & OR exactly one &mut, never both. At any time you may have many shared references or one mutable reference, but not both simultaneously.
Why must the owner be declared with mut to create a &mut reference to it?
- For naming reasons only
- Because a mutable reference can change the value, so the owner must permit mutation
- Because &mut copies the value
- It does not need to be mut
Answer: Because a mutable reference can change the value, so the owner must permit mutation. You can only hand out a &mut to a value whose owner allows mutation, declared with let mut.
What does this print? let s = String::from("hello"); println!("{}", calculate_length(&s)); // fn calculate_length(text: &String) -> usize { text.len() }
- 5
- hello
- &hello
- It does not compile
Answer: 5. text.len() returns the byte length of "hello", which is 5.
What does add_excitement print? let mut m = String::from("hello"); add_excitement(&mut m); println!("{}", m); // fn add_excitement(text: &mut String) { text.push_str("!!!"); }
- hello
- !!!
- hello!!!
- It does not compile
Answer: hello!!!. The &mut reference lets the function append "!!!" in place, so m becomes hello!!!.
Which is valid at the same time?
- Two &mut to the same value
- Several shared & references at once
- One &mut and one & overlapping
- A reference to dropped data
Answer: Several shared & references at once. Multiple shared references are fine together; only a single &mut is exclusive.
How do you call a function declared as fn f(x: &String)?
- f(s)
- f(*s)
- f(mut s)
- f(&s)
Answer: f(&s). Pass a reference with &s; calling f(s) would move the String instead of borrowing it.
Which is more general and usually preferred in function signatures?
- &String
- &str
- String
- Box<String>
Answer: &str. &str is more general; a String coerces to &str, so idiomatic functions take &str.
What guarantee do Rust references always provide?
- They are nullable
- They may dangle
- They always point to valid data (no dangling pointers)
- They are always mutable
Answer: They always point to valid data (no dangling pointers). The borrow checker ensures references never outlive the data they point to.