title: Slices
A slice is a reference to a contiguous sequence of elements in a collection. It lets you borrow a portion of an array or vector without copying it.
Syntax
let a = [1, 2, 3, 4, 5];
let slice = &a[1..3]; // borrows elements at index 1 and 2 -> [2, 3]The type of a slice is &[T] (or &mut [T] for mutable slices).
Range syntax
The start is inclusive and the end is exclusive by default. For an array [10, 20, 30, 40, 50]:
&a[1..4] // [20, 30, 40] — index 1, 2, 3 (NOT 4)
&a[1..=4] // [20, 30, 40, 50] — index 1, 2, 3, 4 (inclusive end with =)
&a[..3] // [10, 20, 30] — from start up to (not including) 3
&a[2..] // [30, 40, 50] — from index 2 to end
&a[..] // [10, 20, 30, 40, 50] — entire collection[!info] Why is the end exclusive? This convention comes from Edsger Dijkstra’s 1982 paper “Why numbering should start at zero.” His argument: with an exclusive end, the length of a range is always
end - start(e.g.4 - 1 = 3elements), and adjacent ranges compose cleanly —[0..4]and[4..8]cover[0..8]with no overlap or gap. The alternative (both inclusive) would require[i..i-1]to express an empty range, which breaks at index 0. Most modern languages (Python, Go, Java) adopted the same convention for these reasons.
String slices
String slices (&str) work the same way:
let s = String::from("hello world");
let hello = &s[0..5]; // "hello"
let world = &s[6..11]; // "world"String literals are already slices — &str is a slice pointing into the binary.
Slices in function signatures
Prefer &[T] over &Vec<T> and &str over &String in function parameters — they’re more flexible since they accept both owned types and slices:
fn sum(numbers: &[i32]) -> i32 {
numbers.iter().sum()
}
let v = vec![1, 2, 3];
sum(&v); // works with Vec
sum(&v[1..]); // works with a slice of VecKey points
- Slices don’t own data, they borrow it
- The borrow checker ensures the original data outlives the slice
- Slices carry both a pointer and a length