TL;DR: A slice (
&[T]) is a view into a contiguous block of memory, it holds a pointer and a length, but doesn't own the data. You get a slice by borrowing a portion of aVec<T>, an array[T; N], or any contiguous collection.&stris a string slice, a&[u8]that's guaranteed to be valid UTF-8. Slices are one of Rust's most important types: functions should accept&[T]instead of&Vec<T>whenever possible.
What Is a Slice?
A slice &[T] is a fat pointer: a pointer to the first element plus a length. It borrows a view into some contiguous data without owning it.
fn main() {
let v = vec![1, 2, 3, 4, 5];
let all: &[i32] = &v; // slice of the entire Vec
let part: &[i32] = &v[1..4]; // slice of elements 1, 2, 3
println!("{:?}", all); // [1, 2, 3, 4, 5]
println!("{:?}", part); // [2, 3, 4]
println!("len = {}", part.len()); // 3
}The slice part borrows from v, it becomes invalid when v is dropped. No data is copied.
How Do You Slice Different Types?
Arrays, Vec, and String all deref to slices, any type that stores contiguous elements can be sliced.
// From an array
let arr = [10, 20, 30, 40];
let slice: &[i32] = &arr[1..3]; // [20, 30]
// From a Vec
let v = vec!["a", "b", "c"];
let slice: &[&str] = &v[..2]; // ["a", "b"]
// String slice (&str)
let s = String::from("hello world");
let word: &str = &s[0..5]; // "hello"
let s_ref: &str = &s; // entire string as &str
// Arrays coerce to slices automatically
fn sum(nums: &[i32]) -> i32 { nums.iter().sum() }
sum(&arr); // [i32; 4] coerces to &[i32]
sum(&v[..]); // Vec<i32> coerces to &[i32]Why Accept &[T] Instead of &Vec<T>?
Functions that accept &[T] work with any contiguous data source: Vec, arrays, and other slices. &Vec<T> forces the caller to have a Vec specifically.
// BAD; only works with Vec<i32>
fn print_nums(v: &Vec<i32>) {
for n in v { print!("{n} "); }
}
// GOOD; works with Vec, arrays, or any slice
fn print_nums(v: &[i32]) {
for n in v { print!("{n} "); }
}
fn main() {
let v = vec![1, 2, 3];
let a = [4, 5, 6];
print_nums(&v); // ✅ Vec coerces to &[i32]
print_nums(&a); // ✅ array coerces to &[i32]
print_nums(&v[1..]); // ✅ partial slice
}The same rule applies to strings: prefer &str over &String in function parameters.
How Does Slice Indexing Work?
Indexing a slice with a single index returns a reference to the element. Indexing with a range returns a sub-slice. Out-of-bounds indexing panics.
let v = vec![10, 20, 30, 40, 50];
let s: &[i32] = &v;
println!("{}", s[0]); // 10; returns &i32, but auto-deref prints value
println!("{:?}", &s[1..3]); // [20, 30]
println!("{:?}", &s[..2]); // [10, 20]; from start
println!("{:?}", &s[3..]); // [40, 50]; to end
// Safe indexing without panicking
match s.get(10) {
Some(val) => println!("{val}"),
None => println!("out of bounds"),
}Use .get(i) when the index might be out of bounds, it returns Option<&T> instead of panicking.
What Are Mutable Slices?
&mut [T] gives mutable access to the elements of a slice, you can modify the values but not the length or allocation.
fn double_all(slice: &mut [i32]) {
for x in slice.iter_mut() {
*x *= 2;
}
}
fn main() {
let mut v = vec![1, 2, 3, 4];
double_all(&mut v);
println!("{:?}", v); // [2, 4, 6, 8]
// Split a mutable slice; get two non-overlapping mutable slices
let (left, right) = v.split_at_mut(2);
left[0] = 99;
right[0] = 100;
println!("{:?}", v); // [99, 4, 100, 8]
}Frequently Asked Questions
[T] is a dynamically-sized type (DST): you can't create a value of type [T] directly because its size isn't known at compile time. &[T] is a fat pointer (pointer + length) to a [T], this has a known size and is how you actually use slices.
&str is a slice of bytes (&[u8]) that is guaranteed to be valid UTF-8. Rust's type system enforces this: you need unsafe to create a &str from raw bytes that haven't been validated. For arbitrary bytes, use &[u8].
Yes, &[] is a valid empty slice. slice.is_empty() checks for this. Empty slices are perfectly safe and have zero length.
Vec<T> implements Deref<Target = [T]>, so &Vec<T> can automatically coerce to &[T] where needed. This deref coercion is why you can pass a &Vec to a function expecting &[T].
Sources
Related Glossary Terms
- Vec:
Vec<T>derefs to&[T], the owned counterpart to slices - String:
Stringderefs to&str, the owned counterpart to string slices - Ownership: Slices are borrowed views, they don't own the data they point to
- Lifetime: A slice's lifetime is tied to the collection it borrows from
- Iterator:
slice.iter()produces an iterator over&Treferences
Keep Reading
- Rust Ownership and Borrowing Explained: slices are borrowed views into owned data
- Rust Lifetimes Deep Dive: slice lifetimes and the borrow checker
- Rust String vs str Explained: str is a slice type; understanding slices explains str

