Borrowing in Rust
In this lesson, you will learn about borrowing, a core concept that works together with ownership to make Rust both safe and efficient.
Borrowing allows you to use a value without taking ownership of it. This prevents unnecessary data movement while keeping memory safe.
What Is Borrowing?
Borrowing means accessing a value through a reference instead of owning it. A reference lets you read or modify data without taking ownership.
In Rust, references are created using the & symbol.
Why Borrowing Is Needed
Without borrowing, every function call would require transferring ownership. This would make programs inefficient and hard to write.
Borrowing allows:
- Reusing data without copying
- Safe access to memory
- Efficient function calls
Immutable Borrowing
An immutable borrow allows you to read a value but not modify it.
fn print_length(s: &String) {
println!("Length: {}", s.len());
}
fn main() {
let text = String::from("Rust");
print_length(&text);
println!("{}", text);
}
The variable text is still valid after the function call because ownership
was not moved.
Multiple Immutable Borrows
Rust allows multiple immutable references at the same time. This is safe because none of them can modify the data.
let s = String::from("Dataplexa");
let r1 = &s;
let r2 = &s;
println!("{}", r1);
println!("{}", r2);
Mutable Borrowing
A mutable borrow allows you to modify a value through a reference.
Mutable references are created using &mut.
fn add_text(s: &mut String) {
s.push_str(" Programming");
}
fn main() {
let mut name = String::from("Rust");
add_text(&mut name);
println!("{}", name);
}
Rules of Borrowing
Rust enforces strict rules to prevent data races:
- You can have multiple immutable references OR
- You can have exactly one mutable reference
- You cannot mix mutable and immutable references at the same time
Invalid Borrowing Example
The following code causes a compile-time error:
let mut s = String::from("Rust");
let r1 = &s;
let r2 = &mut s; // ERROR
println!("{}", r1);
println!("{}", r2);
Rust prevents this to ensure memory safety.
Borrowing and Scope
References are valid only within their scope. Once a reference goes out of scope, Rust allows new borrows.
let mut s = String::from("Hello");
{
let r = &s;
println!("{}", r);
}
let r2 = &mut s;
r2.push_str(" Rust");
Borrowing Prevents Data Races
A data race occurs when multiple references try to access and modify data at the same time.
Rust’s borrowing rules eliminate data races at compile time.
Why Borrowing Is Powerful
Borrowing enables:
- Safe memory access
- High performance
- Clear data flow
- Reliable concurrent code
📝 Practice Exercises
Exercise 1
Create a string and pass it as an immutable reference to a function.
Exercise 2
Create a mutable string and modify it using a mutable reference.
Exercise 3
Try creating two mutable references at the same time and observe the error.
Exercise 4
Demonstrate borrowing with scoped references.
✅ Practice Answers
Answer 1
fn show(s: &String) {
println!("{}", s);
}
Answer 2
fn update(s: &mut String) {
s.push_str("!");
}
Answer 3
// Rust will not allow two mutable references at once
Answer 4
{
let r = &value;
println!("{}", r);
}
What’s Next?
Now that you understand borrowing, the next lesson will introduce references in more detail.
You will learn how references work internally and how they relate to lifetimes.