Slices
Immutable Slices
An immutable slice (or shared slice), written &[T] for element sequences and &str for UTF-8 text, provides read-only, bounds-checked access to a contiguous range of elements without copying or taking ownership of the underlying collection.
Key Properties of &[T]
- Read-only guarantee: While an immutable slice
&[T]is active, neither the slice nor the underlying owner (Vec<T>or[T; N]) can modify the elements or reallocate the buffer. - Freely copyable (
Copy): Because&[T]is a shared 16-byte fat pointer ((ptr, len)),&[T]implementsCopyfor every typeT—even whenTitself is notCopy(such as&[String])! Copying or sub-slicing&[T]only copies the 16-byte(ptr, len)pair on the stack. - Zero-cost abstraction over any contiguous buffer: A single function accepting
&[T]can inspect stack arrays[T; N], heap vectorsVec<T>, boxed slicesBox<[T]>, or empty slices&[].
fn print_first_and_last(slice: &[String]) {
// `slice` is `Copy` even though `String` is not `Copy`!
let copy_of_slice_ref: &[String] = slice;
if let (Some(first), Some(last)) = (copy_of_slice_ref.first(), slice.last()) {
println!("First: '{first}', Last: '{last}' (total = {})", slice.len());
} else {
println!("Slice is empty");
}
}
fn main() {
let tags = [
String::from("rust"),
String::from("systems"),
String::from("memory-safe"),
];
print_first_and_last(&tags);
print_first_and_last(&tags[0..2]);
print_first_and_last(&[]);
}
First: 'rust', Last: 'memory-safe' (total = 3)
First: 'rust', Last: 'systems' (total = 2)
Slice is empty
Non-Mutating Inspection and Search Methods on &[T]
Immutable slices support a rich suite of read-only query, search, and iteration methods:
| Method | Signature / Example | What It Returns |
|---|---|---|
slice.len() / slice.is_empty() |
s.len() |
Number of elements (usize) / true if len == 0 |
slice.get(idx_or_range) |
s.get(2) / s.get(1..3) |
Option<&T> or Option<&[T]> (None if out of bounds) |
slice.first() / slice.last() |
s.first() |
Option<&T> for index 0 or index len - 1 |
slice.split_first() / split_last() |
s.split_first() |
Option<(&T, &[T])> splitting head from tail |
slice.split_at(mid) |
s.split_at(2) |
(&[T], &[T]) splitting [..mid] and [mid..] |
slice.contains(&val) |
s.contains(&42) |
true if any element equals val ($O(n)$ linear scan) |
slice.starts_with(sub) / ends_with(sub) |
s.starts_with(&[1, 2]) |
true if prefix or suffix matches sub |
slice.binary_search(&val) |
s.binary_search(&42) |
Ok(idx) if found or Err(insert_idx) ($O(\log n)$ on sorted slice) |
slice.windows(n) |
s.windows(2) |
Iterator over overlapping sub-slices &[T] of length n |
slice.chunks(n) / chunks_exact(n) |
s.chunks(3) |
Iterator over non-overlapping sub-slices &[T] of length up to n |
fn main() {
let prices: &[u32] = &[10, 20, 35, 50, 80];
// 1. Sliding windows of size 2 to compute consecutive differences:
let diffs: Vec<u32> = prices.windows(2).map(|w| w[1] - w[0]).collect();
println!("Consecutive price jumps: {diffs:?}");
// 2. Fast O(log n) binary search on a sorted slice:
println!("binary_search(35): {:?}", prices.binary_search(&35));
println!("binary_search(40): {:?}", prices.binary_search(&40)); // Err(3) = insertion index!
}
Consecutive price jumps: [10, 15, 15, 30]
binary_search(35): Ok(2)
binary_search(40): Err(3)
Iterating Over an Immutable Slice (for x in slice)
Iterating over slice: &[T] yields shared references &T to each element. For Copy element types (such as i32), you can either pattern-match the reference in the loop header (for &n in slice) or call slice.iter().copied():
fn main() {
let values: &[i32] = &[5, 10, 15];
let mut sum = 0;
for &n in values {
// `&n` destructures `&i32` so `n` has type `i32`:
sum += n;
}
println!("Sum: {sum}");
}
Sum: 30