References and Borrowing Explained
Learn about references and borrowing, allowing multiple parts of your code to access data without taking ownership, safely.
References and Borrowing Explained is a free Learn Rust Coding lesson on CoddyKit — lesson 2 of 3. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Learn Rust Coding learning path, one of 3 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What are References?
In Rust, variables own their data. But what if you need to let other parts of your code look at or even change that data without taking ownership?
That's where references come in! A reference is like a pointer to a value, but with strict rules enforced by the Rust compiler.
Think of it as looking at something through a window instead of owning the item itself.
Your First Reference
We create a reference using the & operator. This creates a non-owning pointer to a value. Let's see it in action:
fn print_length(s: &String) {
println!("The length is: {}", s.len());
}
fn main() {
let message = String::from("Hello, CoddyKit!");
print_length(&message); // Pass a reference
println!("Original message still here: {}", message);
}Understanding Borrowing
When you create a reference to a value, you are borrowing that value. The owner still owns it, but you get temporary access.
- Lending without giving away: You can use the borrowed data.
- No ownership transfer: The original variable still owns the data and will drop it when it goes out of scope.
- Safety first: Rust's borrowing rules prevent common programming errors like data races.
Immutable Borrows: Read-Only Access
By default, references in Rust are immutable. This means you can read the data they point to, but you cannot change it.
This is a core safety feature! If multiple parts of your code have read-only access, they can't accidentally interfere with each other's data.
Immutable Borrow in Practice
Here, the calculate_sum function takes an immutable reference to a vector. It can read the elements but cannot add or remove them.
fn calculate_sum(numbers: &Vec<i32>) -> i32 {
let mut total = 0;
for num in numbers {
total += num;
}
total
}
fn main() {
let my_numbers = vec![10, 20, 30, 40];
let sum = calculate_sum(&my_numbers); // Immutable borrow
println!("The sum is: {}", sum);
println!("Original vector: {:?}", my_numbers);
}Mutable Borrows: Changing Data
Sometimes, you need to modify data that you've borrowed. For this, you use a mutable reference, denoted by &mut.
Mutable references come with a crucial rule: you can only have one mutable reference to a particular piece of data at a time. This prevents data races and ensures safety.
Modifying Data with &mut
The add_suffix function takes a mutable reference to a String. It can modify the original String directly.
fn add_suffix(text: &mut String) {
text.push_str(" (modified)");
}
fn main() {
let mut my_string = String::from("Original text");
add_suffix(&mut my_string); // Mutable borrow
println!("Modified string: {}", my_string);
}The Golden Rules of Borrowing
Rust's compiler enforces these rules at compile time to guarantee memory safety:
- You can have one mutable reference to a piece of data at a time.
- OR, you can have any number of immutable references at a time.
- You cannot have a mutable reference while there are active immutable references.
- References must always be valid (they can't outlive the data they point to).
These rules prevent data races and ensure your programs are safe and predictable.
Conflicting Borrows
This code attempts to create both an immutable and a mutable reference to my_value at the same time, which violates Rust's borrowing rules. It will NOT compile.
Try to run it and see the compiler error!
fn main() {
let mut my_value = 100;
let r1 = &my_value; // Immutable reference
let r2 = &mut my_value; // Mutable reference (problem here!)
println!("r1: {}", r1);
// println!("r2: {}", r2); // This line would also cause an error if uncommented
}Check Your Understanding
Consider the following Rust code snippet:
fn process_data(data: &mut Vec<i32>) {
data.push(4);
}
fn main() {
let mut numbers = vec![1, 2, 3];
let first_ref = &numbers[0]; // Line A
process_data(&mut numbers); // Line B
println!("First element: {}", first_ref); // Line C
}Which line(s) will cause a compile-time error due to Rust's borrowing rules?
Recap: References & Borrowing
Great job! You've learned the fundamentals of references and borrowing in Rust:
- References (
&) allow you to access data without taking ownership. - Borrowing is the act of creating a reference, lending access to data.
- Immutable references (
&) provide read-only access. - Mutable references (
&mut) provide read/write access. - Rust's strict borrowing rules (one mutable OR many immutable) prevent data races and ensure memory safety at compile time.
These concepts are crucial for writing safe and efficient Rust code!
Frequently asked questions
Is the “References and Borrowing Explained” lesson free?
Yes — the full text of “References and Borrowing Explained” is free to read here on the web, and the Learn Rust Coding course includes 3 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Learn Rust Coding course, upgrade to CoddyKit PRO.
What will I learn in “References and Borrowing Explained”?
Learn about references and borrowing, allowing multiple parts of your code to access data without taking ownership, safely. You practise Learn Rust Coding with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Learn Rust Coding?
No prior experience is required. Learn Rust Coding on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 3, so you can start here or from the beginning and move at your own pace.
How long does the “References and Borrowing Explained” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Learn Rust Coding lesson?
Yes. Every Learn Rust Coding lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Understanding Rust's Ownership Model
- References and Borrowing Explained
- Lifetimes for Safe References