A reference, written &T, lets code use a value without taking ownership of it — "borrowing" it temporarily. Rust’s borrowing rule, enforced by the compiler’s borrow checker: at any given time, a value can have either exactly one mutable reference (&mut T) or any number of immutable references (&T), but never both simultaneously. A slice (&[T], or &str for strings) is a reference to a contiguous portion of a collection, without owning or copying the underlying data.
Borrowing with & instead of taking ownership
Passing &value to a function lets it read the value without taking ownership — the caller keeps using the original afterward, unlike passing the value itself, which would move it.
fn calculate_length(s: &String) -> usize { // borrows, doesn't own
s.len()
}
let s1 = String::from("hello");
let len = calculate_length(&s1); // pass a reference
println!("{} is {} chars", s1, len); // s1 still usable here!The one rule: one mutable XOR many immutable
This single rule is what Rust checks at compile time to guarantee no data races: you cannot have a mutable reference alive at the same time as any other reference to the same data.
let mut s = String::from("hello");
let r1 = &s; // OK: immutable borrow
let r2 = &s; // OK: another immutable borrow, allowed
println!("{} {}", r1, r2);
let r3 = &mut s; // OK now that r1, r2 are no longer used
r3.push_str(" world");
// let bad = &s; // if r3 were still in use here: COMPILE ERRORSlices: a view into part of a collection
A slice references a contiguous range without copying it — &str (a string slice) is the most common example, and &[T] works the same way for any collection.
let s = String::from("hello world");
let hello = &s[0..5]; // slice: "hello", no copy of the data
let world = &s[6..11]; // slice: "world"
let numbers = [1, 2, 3, 4, 5];
let middle = &numbers[1..4]; // &[2, 3, 4]