References & Slices
References borrow data without taking ownership. Slices (&[T], &str) are borrowed views into contiguous sequences with runtime length.
Recipe
fn first_word(s: &str) -> &str {
match s.find(' ') {
Some(i) => &s[..i],
None => s,
}
}
fn main() {
let sentence = String::from("hello world");
println!("{}", first_word(&sentence));
}When to reach for this: Function parameters that only read data, substring extraction, and passing array views without copying.
Working Example
fn sum_slice(nums: &[i32]) -> i32 {
nums.iter().sum()
}
fn window(data: &[i32], start: usize, len: usize) -> Option<&[i32]> {
data.get(start..start + len)
}
fn main() {
let arr = [10, 20, 30, 40];
println!("sum={}", sum_slice(&arr));
println!("{:?}", window(&arr, 1, 2));
}What this demonstrates:
&[i32]accepts arrays andVecvia coercion- Range slicing
start..endon arrays and strings .get(range)returnsOptioninstead of panicking&strfrom&Stringvia deref coercion
Deep Dive
Reference Types
| Type | Access | Count at once |
|---|---|---|
&T | Shared read | Many |
&mut T | Exclusive write | One (no & simultaneously) |
String Slices
let s = String::from("hello");
let slice: &str = &s[0..2]; // "he" - byte indicesUTF-8 means byte indices must fall on char boundaries or slicing panics.
Array Slices
let a = [1, 2, 3, 4];
let mid: &[i32] = &a[1..3]; // [2, 3]Slice Patterns
fn head(tail: &[i32]) -> Option<(&i32, &[i32])> {
tail.split_first()
}Gotchas
- Slicing strings at arbitrary byte index - May split a UTF-8 codepoint. Fix: Use
.chars()orunicode-segmentationfor graphemes. - Returning slice tied to local
String- Lifetime error. Fix: Return ownedStringor take owner as parameter. - Confusing
&Vec<T>with&[T]- Prefer&[T]in APIs. Fix: Use slice form;&veccoerces automatically. - Mutable slice aliasing - Two
&mutto overlapping ranges fails. Fix: Split withsplit_at_mut. - Index out of bounds -
arr[99]panics. Fix: Use.get(99).
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
Owned String/Vec | Caller transfers ownership | Read-only inspection suffices |
Cow<'a, str> | Maybe borrow, maybe allocate | Ownership always clear |
| Indices + length | FFI interop | Safe Rust APIs |
| Iterators | Lazy traversal | Random access needed |
FAQs
What is deref coercion?
&String coerces to &str, &Vec<T> to &[T] at function call sites when the target expects a slice.
Can slices be empty?
Yes. &[] and &s[0..0] are valid empty slices.
How long can a slice be?
len() is usize. Slices cannot exceed the underlying allocation.
Are slice elements copied on pass-by-value?
Slices are references (fat pointers). Elements are not copied.
Can I mutate through `&mut [T]`?
Yes. slice[0] = 1 mutates the underlying array or Vec.
What is `as_ptr` for?
Low-level pointer to first element. Used in unsafe/FFI, not typical application code.
How do I copy a slice?
slice.to_vec() for owned Vec. slice.to_owned() when T: Clone.
Can `&str` be null?
Not in safe Rust. Use Option<&str> for absence.
What is a wide pointer?
Slice/str fat pointer stores data pointer plus length (two words).
How do lifetimes appear here?
&str in a return type needs a lifetime tied to input. See the Ownership section.
Related
- Ownership Preview - move vs borrow
- Borrowing & References - aliasing rules
- Vec & Slices - vector operations
- UTF-8 & Indexing - safe string slicing
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.