Taking slices instead of owned collections makes your functions both cheaper and more flexible.
A slice is a borrowed view into a contiguous sequence: a pointer plus a length. &[T] views elements of an array or Vec<T>, and &str views UTF-8 bytes of a String or string literal. Slices do not own anything, so they follow the borrowing rules.
Create them with range syntax: &v[1..3] (elements 1 and 2), &v[..2], &v[2..], &v[..]. Out-of-range bounds panic.
String literals like "hello" have type &'static str: a slice pointing into the program binary, valid for the whole run.
The most useful habit is in function signatures. Prefer:
&strover&String,&[T]over&Vec<T>.
Thanks to deref coercion, a &String automatically converts to &str and a &Vec<T> to &[T], so the slice version accepts strictly more callers, including literals and arrays.
Because a slice borrows from its source, the compiler keeps the source alive and unmodified while the slice is in use. A classic example: if a function returns &str pointing at the first word of a String, you cannot clear() the string while still using that word.
One gotcha: string slice indices are byte offsets. Slicing in the middle of a multi-byte UTF-8 character, like &"héllo"[0..2], panics at runtime. When working with text, prefer methods such as split_whitespace, find or char_indices that return valid boundaries.
&mut [T] is the mutable counterpart, letting you modify elements in place (for example slice.sort()), though it cannot change the length.
Example
fn first_word(s: &str) -> &str {
match s.find(' ') {
Some(i) => &s[..i],
None => s,
}
}
fn sum(xs: &[i32]) -> i32 {
xs.iter().sum()
}
fn main() {
let owned = String::from("hello world");
println!("{}", first_word(&owned)); // hello (&String -> &str)
println!("{}", first_word("one-word")); // one-word (literal is &str)
let v = vec![1, 2, 3, 4];
let arr = [10, 20];
println!("{} {}", sum(&v[1..3]), sum(&arr)); // 5 30
let mut nums = [3, 1, 2];
nums[..].sort(); // &mut [i32]
println!("{nums:?}"); // [1, 2, 3]
}Remember
- A slice is a borrowed pointer + length:
&[T]or&str - Accept
&strand&[T]in signatures;&Stringand&Veccoerce to them - String slice indices are byte offsets and must land on char boundaries
Watch out for
- Slicing text by byte index and panicking on non-ASCII characters.