References: Borrow Without Owning
References let you use a value without taking ownership. The & creates a reference; using it is called borrowing.
fn length(s: &String) -> usize {
s.len() // s is borrowed - NOT dropped when this ends
}
fn main() {
let s = String::from("hello world");
let len = length(&s); // Pass reference
println!("'{}' has {} chars", s, len); // s still valid!
}
// Borrowing rules:
// 1. Any number of immutable references (&T) at once
// 2. OR exactly one mutable reference (&mut T)
// 3. Never both simultaneously
fn demo_rules() {
let s = String::from("hi");
let r1 = &s;
let r2 = &s;
let r3 = &s;
println!("{} {} {}", r1, r2, r3); // Fine - all immutable
// All references done here (NLL)
let mut s2 = String::from("hi");
let r = &mut s2;
r.push_str(", world");
println!("{}", r); // Fine - only one mutable ref
}Why These Rules?
fn main() {
// This pattern is unsafe in C++ - Rust prevents it:
let mut v = vec![1, 2, 3];
// let first = &v[0]; // immutable ref
// v.push(4); // WOULD invalidate first! vector may reallocate
// println!("{}", first); // dangling pointer in C++!
// Rust forces you to be safe:
let first = &v[0];
println!("first: {}", first); // use reference BEFORE mutating
v.push(4); // Now safe - first is done being used
// Auto-deref: Rust dereferences automatically
let s = String::from("hello");
let r = &s;
println!("{}", r.len()); // Auto-deref: same as (*r).len()
// Deref coercions:
let s: String = String::from("hi");
let _r: &str = &s; // &String -> &str automatically
}🎯 Practice
- Write fn count_vowels(s: &str) -> usize counting vowels without taking ownership
- Write fn summarize(s: &String) -> String returning first 50 chars
- Demonstrate the borrow checker: hold a &v[0] while trying to push to v - read the error
🎉 Key Takeaways
- &T borrows immutably; any number allowed simultaneously
- &mut T borrows mutably; only one at a time, no mixing with immutable
- NLL: borrows end at last use, not at closing brace
- Deref coercions: &String automatically becomes &str