Rust Borrowing
Borrowing lets code access a value through a reference without taking ownership of it, and the borrow checker enforces strict rules about how references can be used.
What Is a Reference?
A reference lets you refer to a value without taking ownership of it. References are created with the `&` operator and are said to "borrow" the value they point to. Because borrowing does not transfer ownership, the original owner remains responsible for dropping the value, and the borrowed data must not be used after the owner goes out of scope.
Borrowing with an Immutable Reference
fn calculate_length(text: &String) -> usize {
text.len() // reading through the reference, not owning it
}
fn main() {
let message = String::from("borrowed");
let length = calculate_length(&message);
println!("'{}' has length {}", message, length); // still valid
}Mutable References
A mutable reference, written `&mut T`, allows the borrowed code to modify the value it points to. To create one, both the original variable and the reference must be declared `mut`. Mutable references let you pass a value into a function for modification without transferring ownership or requiring the function to return it back.
Borrowing with a Mutable Reference
fn append_greeting(text: &mut String) {
text.push_str(", hello!");
}
fn main() {
let mut message = String::from("hi there");
append_greeting(&mut message);
println!("{}", message);
}The Borrowing Rules
The borrow checker enforces two rules for any given value, at any given point in the program: you may have any number of immutable references (`&T`) at once, OR exactly one mutable reference (`&mut T`), but never both kinds at the same time. Additionally, references must always be valid — Rust will not allow a reference to outlive the data it points to, which rules out dangling references entirely.
- Any number of immutable references (`&T`) may coexist simultaneously.
- Only one mutable reference (`&mut T`) may exist at a time, with no other references active.
- Immutable and mutable references to the same value cannot coexist.
- A reference's scope ends at its last use, not necessarily at the closing brace (non-lexical lifetimes).
- The borrow checker rejects any reference that could outlive the data it refers to.
Multiple Immutable Borrows Are Fine
fn main() {
let numbers = vec![1, 2, 3, 4];
let first_half = &numbers[..2];
let second_half = &numbers[2..];
println!("{:?} and {:?}", first_half, second_half);
// multiple immutable borrows of `numbers` coexist safely
}Dangling References
Rust's compiler statically prevents dangling references — references to memory that has already been freed. If a function tries to return a reference to a value it created locally, the compiler rejects the code at compile time, because that local value would be dropped when the function returns, leaving the reference pointing at freed memory.
The Compiler Rejects Dangling References
// fn dangle() -> &String {
// let s = String::from("oops");
// &s // error: `s` is dropped at the end of this function
// }
fn not_dangling() -> String {
let s = String::from("safe");
s // ownership moves out instead of a reference
}
fn main() {
let value = not_dangling();
println!("{}", value);
}Exercise: Rust Borrowing
What does creating a reference with `&value` do?