Slice Patterns
Slice patterns allow match, if let, and let expressions to match on the length and elements of an array or slice (&[T] or &mut [T]) and destructure its prefix, suffix, and middle subslice in a single declarative pattern.
Slice Pattern Syntax Forms
In a slice pattern [p1, p2, ..], fixed element patterns match individual elements by position, while the rest pattern .. matches zero or more remaining elements. At most one .. rest pattern may appear in a single slice pattern.
| Pattern Syntax | Matches Slices of Length | What It Binds |
|---|---|---|
[] |
Exactly 0 (empty slice) |
Nothing |
[only] |
Exactly 1 |
only = single element |
[first, second] |
Exactly 2 |
first, second |
[first, ..] |
>= 1 |
first = index 0; ignores remaining elements |
[first, rest @ ..] |
>= 1 |
first = index 0; rest = subslice &s[1..] |
[init @ .., last] |
>= 1 |
init = subslice &s[..len - 1]; last = final element |
[first, middle @ .., last] |
>= 2 |
first = index 0, last = final element, middle = &s[1..len - 1] |
Destructuring `s = &[10, 20, 30, 40, 50]` with `[first, middle @ .., last]`:
+----+----+----+----+----+
| 10 | 20 | 30 | 40 | 50 |
+----+----+----+----+----+
^ \_________/ ^
first middle last
(&10) (&[20,30,40]) (&50)
Matching Fixed and Variable-Length Slices
When matching a dynamically sized slice &[T], the compiler requires the match arms to be exhaustive across all possible lengths (0, 1, 2, ..., N):
fn describe_slice(values: &[i32]) {
match values {
[] => println!("Empty slice"),
[single] => println!("Single element: {single}"),
[first, second] => println!("Two elements: {first} and {second}"),
[0, rest @ ..] => println!("Starts with zero, followed by: {rest:?}"),
[first, middle @ .., last] => {
println!("First = {first}, middle ({} items) = {middle:?}, last = {last}", middle.len());
}
}
}
fn main() {
describe_slice(&[]);
describe_slice(&[42]);
describe_slice(&[1, 2]);
describe_slice(&[0, 5, 6, 7]);
describe_slice(&[10, 20, 30, 40, 50]);
}
Empty slice
Single element: 42
Two elements: 1 and 2
Starts with zero, followed by: [5, 6, 7]
First = 10, middle (3 items) = [20, 30, 40], last = 50
Parsing Command-Line or Protocol Tokens with Slice Patterns
Converting a Vec<String> to a slice of &str (or matching a byte slice &[u8]) enables concise command and binary header parsing without manual length checks or index bounds arithmetic:
fn execute_command(tokens: &[&str]) {
match tokens {
["quit" | "exit"] => println!("Shutting down."),
["get", key] => println!("Fetching key '{key}'"),
["set", key, value] => println!("Setting '{key}' = '{value}'"),
["push", list, items @ ..] if !items.is_empty() => {
println!("Pushing {} item(s) {:?} onto '{list}'", items.len(), items);
}
_ => println!("Unrecognized command: {tokens:?}"),
}
}
fn main() {
execute_command(&["get", "session_id"]);
execute_command(&["set", "timeout", "30s"]);
execute_command(&["push", "jobs", "task_a", "task_b", "task_c"]);
execute_command(&["exit"]);
}
Fetching key 'session_id'
Setting 'timeout' = '30s'
Pushing 3 item(s) ["task_a", "task_b", "task_c"] onto 'jobs'
Shutting down.
In-Place Mutation with Mutable Slice Patterns
Matching on a mutable slice &mut [T] binds element variables as &mut T and @ .. subslice bindings as &mut [T], allowing safe simultaneous mutation of disjoint parts of a slice:
fn swap_ends_and_zero_middle(slice: &mut [i32]) {
if let [first, middle @ .., last] = slice {
std::mem::swap(first, last);
middle.fill(0);
}
}
fn main() {
let mut data = [1, 2, 3, 4, 5];
swap_ends_and_zero_middle(&mut data);
println!("After mutation: {data:?}");
}
After mutation: [5, 0, 0, 0, 1]
Irrefutable Array Patterns vs. Refutable Slice Patterns
Because a fixed-size array [T; N] has a length known at compile time, a pattern with N elements (or using ..) is irrefutable and can be used directly in a let statement (let [a, .., z] = array;). By contrast, a slice &[T] has an unknown runtime length, so any pattern other than [..] or a single identifier is refutable and must be matched with match, if let, or let ... else:
// ❌ does not compile
fn first_two(s: &[i32]) {
let [a, b, ..] = s;
println!("{a}, {b}");
}
fn main() {}
error[E0005]: refutable pattern in local binding
--> src/main.rs:2:9
|
2 | let [a, b, ..] = s;
| ^^^^^^^^^^ pattern `&[]` and `&[_]` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
help: you might want to use `let else` to handle the variant that isn't matched
|
2 | let [a, b, ..] = s else { todo!() };
| ++++++++++++++++