Rust Slice Patterns
A slice pattern matches an array or a slice with square brackets. It tests the length and binds the ends in one step.
The long way: check the length, then index
Without a slice pattern, binding both ends takes a length check and two index expressions:
let nums = [1, 2, 3, 4];
if nums.len() >= 2 {
println!("first {}, last {}", nums[0], nums[nums.len() - 1]);
} else {
println!("too short");
}Match by length
Square brackets match a slice piece by piece. The empty pattern fits only an empty slice. [only] fits exactly one item:
let nums: &[i32] = &[7];
match nums {
[] => println!("empty"),
[only] => println!("single {only}"),
_ => println!("several"),
}Bind both ends with ..
Two dots inside the brackets skip the middle, however long it is. This pattern fits any length of 2 or more, and binds both ends at once:
let nums = [1, 2, 3, 4];
match &nums[..] {
[first, .., last] => println!("first {first}, last {last}"),
_ => println!("too short"),
}First item, or nothing
[first, ..] tests "not empty" and takes the first item in one step. A short list simply does not match — it never panics:
let nums: &[i32] = &[];
if let [head, ..] = nums {
println!("head {head}");
} else {
println!("nothing here");
}Save the middle as its own slice
Put a name and @ in front of the dots. The skipped middle is saved as a slice of its own:
let nums = [10, 20, 30, 40];
if let [head, middle @ .., tail] = &nums[..] {
println!("head {head}, middle {middle:?}, tail {tail}");
}Pull a fixed array apart in a let
An array has a fixed length, known before the program runs. So a plain let can pull it apart. No match is needed:
let [a, b, c] = [5, 6, 7];
println!("{a} {b} {c}");Example
fn describe(nums: &[i32]) -> String {
match nums {
[] => String::from("empty"),
[only] => format!("single {}", only),
[first, .., last] => format!("first {} last {}", first, last),
}
}
fn main() {
println!("{}", describe(&[]));
println!("{}", describe(&[7]));
println!("{}", describe(&[1, 2, 3]));
let scores = [10, 20, 30, 40];
if let [head, rest @ ..] = &scores[..] {
println!("head {} rest {:?}", head, rest);
}
}Key points
- .. may appear only once in a slice pattern.
- A second one is rejected.
- Rust cannot tell where the middle would end.
- A slice has no fixed length.
- So slice patterns live in match, if let, and while let.
- In those places, a non-fit has somewhere to go.
In this example, describe sorts a slice by shape. The empty slice fits [], so it gives "empty". The slice [7] fits [only], so it gives "single 7". The slice [1, 2, 3] fits [first, .., last]: first is 1, last is 3, and .. quietly covers the 2 in the middle. Below, &scores[..] is the whole array borrowed as a slice. The if let pattern [head, rest @ ..] binds head to 10 and saves the rest as a new slice, [20, 30, 40]. One pattern did the length test and the splitting together.
Note
Unlike indexing with [0] and [1], a slice pattern never panics on a short list.
A short list simply does not match, and the next arm gets its turn.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
Which pattern matches any non-empty slice and binds its first item?