Borrowing in Rust lets you access a value without taking ownership of it. You do this using references. A reference is like a pointer that allows read or write access to a value owned by another variable, without moving or cloning the data.
When you pass a value to a function in Rust, ownership is usually moved. That means the original variable becomes invalid unless you explicitly return the value. But sometimes you just want to read or modify a value without taking it over. That is where borrowing comes in.
This post explains how references work, how borrowing keeps your data safe, and how Rust uses these rules to prevent common bugs found in other languages.
If you have not read Understanding Ownership in Rust, do that first. Borrowing only makes sense once you understand ownership.
What is a Reference?
A reference is a way to borrow a value without taking ownership of it. You create a reference using the ampersand symbol.
Example
let name = String::from("Alice");
let name_ref = &name;
Here, name_ref is a reference to name. The value is not moved or cloned. You can still use name afterward.
You can pass a reference to a function too:
fn greet(name: &String) {
println!("Hello, {}", name);
}
let person = String::from("Alice");
greet(&person);
This is borrowing. The function can read person, but it does not own it.
Mutable vs Immutable References
Rust has two kinds of references:
- Immutable reference: &T (read-only access)
- Mutable reference: &mut T (allows modification)
You can have many immutable references at the same time:
let message = String::from("Hello");
let a = &message;
let b = &message;
But you can only have one mutable reference at a time:
let mut message = String::from("Hello");
let m = &mut message;
This rule avoids data races and makes sure your program is safe even when accessing memory from multiple parts of your code.
Rust does not allow mutable and immutable references to exist at the same time.
This will fail
let mut message = String::from("Hello");
let a = &message;
let b = &mut message; // cannot borrow as mutable while already borrowed as immutable
Modifying Data Through Mutable References
Use a mutable reference when you want to change the value:
fn add_suffix(s: &mut String) {
s.push_str(" world");
}
let mut msg = String::from("Hello");
add_suffix(&mut msg);
println!("{}", msg); // prints "Hello world"
Notice that you must declare the original variable as mutable and also pass it as a mutable reference.
References in Function Return
You can return references from functions, but only if the referenced data lives long enough. We will cover lifetimes later, but here is one safe case:
fn first_char(s: &String) -> &str {
&s[0..1]
}
Here, the returned reference is tied to the input reference, so it is valid.
Summary Table
Action | Syntax | Ownership Moves? |
---|---|---|
Pass value | T | Yes |
Borrow immutable | &T | No |
Borrow mutable | &mut T | No |
Final Word
Borrowing in Rust allows safe access to values without transferring ownership. You use & to read and &mut to write. Rust enforces strict rules to prevent data races and make sure your program behaves safely, even in complex situations.