References and Borrowing
References allow using values without taking ownership. A reference is guaranteed to point to a valid value for its lifetime.
fn main() { let s1 = String::from("hello"); let len = calculate_length(&s1); println!("The length of '{s1}' is {len}."); } fn calculate_length(s: &String) -> usize { s.len() }
The &
syntax creates a reference. &s1
refers to s1
without owning it. When the reference goes out of scope, the value isn’t dropped because the reference doesn’t own it.
fn main() { let s1 = String::from("hello"); let len = calculate_length(&s1); println!("The length of '{s1}' is {len}."); } fn calculate_length(s: &String) -> usize { // s is a reference to a String s.len() } // Here, s goes out of scope. But because s does not have ownership of what // it refers to, the String is not dropped.
Creating a reference is called borrowing. References are immutable by default:
fn main() {
let s = String::from("hello");
change(&s);
}
fn change(some_string: &String) {
some_string.push_str(", world");
}
Mutable References
Use &mut
for mutable references:
fn main() { let mut s = String::from("hello"); change(&mut s); } fn change(some_string: &mut String) { some_string.push_str(", world"); }
Borrowing rules:
- Only one mutable reference per value at a time
- Cannot mix mutable and immutable references in overlapping scopes
This prevents data races at compile time:
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
println!("{r1}, {r2}");
}
Multiple mutable references are allowed in separate scopes:
fn main() { let mut s = String::from("hello"); { let r1 = &mut s; } // r1 goes out of scope here, so we can make a new reference with no problems. let r2 = &mut s; }
Cannot combine mutable and immutable references:
fn main() {
let mut s = String::from("hello");
let r1 = &s; // no problem
let r2 = &s; // no problem
let r3 = &mut s; // BIG PROBLEM
println!("{r1}, {r2}, and {r3}");
}
Reference scopes end at their last usage, not at the end of the block:
fn main() { let mut s = String::from("hello"); let r1 = &s; // no problem let r2 = &s; // no problem println!("{r1} and {r2}"); // Variables r1 and r2 will not be used after this point. let r3 = &mut s; // no problem println!("{r3}"); }
Dangling References
Rust prevents dangling references through compile-time checks:
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> &String {
let s = String::from("hello");
&s
}
Error: function returns a borrowed value with no value to borrow from.
Solution—return the value directly:
fn main() { let string = no_dangle(); } fn no_dangle() -> String { let s = String::from("hello"); s }
The Rules of References
- At any time: either one mutable reference OR any number of immutable references
- References must always be valid