In Rust, references allow you to refer to a value without taking ownership of it. This mechanism is central to Rust’s memory safety guarantees.
Passing Referneces to Functions
When passing data to a function without transferring ownership, you use a reference:
fn main() {
let mut text = String::from("Rust references and borrowing");
println!("{}", text);
let length = modify_and_measure(&mut text);
println!("==={}===", length);
}
fn modify_and_measure(s: &mut String) -> usize {
s.push_str("===>HelloWorld!");
println!("Modified string: {}", s);
s.len()
}
Here, &mut String indicates a mutable reference—allowing the function to modify the data while ensuring only one such reference exists at a time.
Mutablle Borrowing Rules
Rust enforces that within a single scope, only one mutable reference to a particular piece of data may exist:
let mut data = String::from("example");
let ref_a = &mut data;
// let ref_b = &mut data; // ❌ Error: cannot borrow `data` as mutable more than once
println!("Scope 1: {}", ref_a);
{
let ref_b = &mut data; // ✅ OK: new scope
println!("Scope 2: {}", ref_b);
}
This prevents data races at compile time.
Immutable vs. Mutable References
Multiple immutable references can coexist, but they cannot coexist with a mutable reference:
let mut content = String::from("shared");
let r1 = &content;
let r2 = &content;
println!("Immutable refs: {}, {}", r1, r2);
let r3 = &mut content; // ✅ OK: r1 and r2 are no longer used after this point
println!("Mutable ref: {}", r3);
// println!("{},{},{}", r1, r2, r3); // ❌ Error: cannot use immutable refs while mutable ref is active
The compiler tracks lifetimes and usage to enforce these rules.
Avoiding Dangling References
Rust prevents dangling references by ensuring that a reference cannot outlive the data it points to:
// ❌ This won't compile:
// fn dangling_ref() -> &String {
// let s = String::from("hello");
// &s
// }
// ✅ Return owned data instead:
fn safe_return() -> String {
let s = String::from("Hello");
s
}
Attempting to return a reference to a local variable vioaltes Rust’s lifetime rules, as the variable is dropped when the function ends.
These borrowing rules—combined with Rust’s ownership model—eliminate entire classes of memory bugs without requiring a garbage collector.