Rust Slices (&[T]): Views Without Ownership
A Rust slice is a borrowed view into a contiguous sequence of data, like an array or Vec, without taking ownership. Use it to write functions that operate on parts of a collection efficiently. The footgun: a slice cannot outlive the data it points to.
WHY IT EXISTS Slices exist to create a common, efficient interface for functions that need to read or modify a sequence of elements. Without them, you'd need separate functions for Vec<T>, arrays [T; N], and other collection types, leading to code duplication and less flexible APIs.
THE MENTAL MODEL Think of a slice as a bookmark with a note: "start at this page and read for 10 pages." The slice doesn't own the book (the original data, like a Vec); it's just a temporary, bounded reference. If the book is destroyed, the bookmark is useless. Internally, a slice &[T] is a "fat pointer" containing a memory address and a length.
HOW IT WORKS A slice type, written [T], is a Dynamically Sized Type (DST) because its length isn't known at compile time. You can't have a variable of type [T] directly on the stack. Instead, you must use it behind a pointer, like &[T] (a shared slice) or &mut [T] (a mutable slice). These pointers are "fat," storing both the address of the first element and the slice's length. The Rust borrow checker is the key safety mechanism, ensuring a slice cannot exist longer than the data it borrows from.
WHEN TO USE IT Prefer passing &[T] to functions over &Vec<T>. This makes your function more generic, as it can then accept a view into a Vec, an array, or even another slice. It's the idiomatic way to allow functions to read or modify a sequence without taking ownership. String slices (&str) are a specific kind of slice for UTF-8 text.
WHEN NOT TO USE IT Do not use a slice when a function needs to take ownership of the data, for example, to return it or store it in a struct that will outlive the current scope. In those cases, pass the Vec<T> or Box<[T]> itself. You also cannot return a slice of data that was created inside the function, as that would be a dangling reference.
ONE CANONICAL EXAMPLE A single function can operate on slices from different data sources. Consider a function fn sum(numbers: &[i32]) -> i32. You can call this function with a reference to a Vec<i32>, like sum(&my_vec), or a subsection, like sum(&my_vec[1..3]). You can also call it with a reference to an array, like sum(&my_array). The function is generic over the data's origin, as long as it's a contiguous sequence of i32s.
Read the original → doc.rust-lang.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.