Rustlers Atom 2.3: Mutable and immutable borrow
April 18, 2026•326 words
Owning values is great, but moving them around constantly is stupid. Often, you want to let a function use a value without taking responsibility for freeing it. In Rust, this is called borrowing. You borrow a value by creating a reference. References are pointers that are guaranteed to be valid and align with Rust’s safety rules. There are two types.
Immutable Reference &T
An immutable reference allows you to read data but not change it. You create one by adding & before the variable.
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1); // Pass a reference
println!("The length of '{}' is {}.", s1, len); // s1 is still valid here
}
fn calculate_length(s: &String) -> usize {
s.len()
} // s goes out of scope, but nothing happens because s didn't own the data
You can have as many immutable references to a value as you like, simultaneously. It’s like a read-only Google Doc: everyone can look, but nobody can type.
Mutable Reference &mut T
If you want a function to modify the borrowed value, you use a mutable reference. You create it with &mut. To create a &mut reference, the owner variable must also be declared as mut.
fn main() {
let mut s = String::from("hello");
change(&mut s);
println!("{}", s); // "hello, world"
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
Dereferencing
Under the hood, references are pointers. To access the data they point to, you technically need to dereference them using *. However, Rust has a feature called auto-dereferencing for method calls and field access.
let x = 5;
let y = &mut x;
*y += 1; // Explicit dereference needed to change the integer value
For most dot-methods (s.len(), v.push()), Rust handles the pointer logic for you automatically.