Skip to content

← Shape Your Data: Structs, Enums, Pattern Matching step 12 of 26

Medium Primitives

Slice and array patterns, and rest @ ..

Two functions, both graded.

pub fn shrink(tokens: Vec<String>) -> String
pub fn windows_sum(v: Vec<i64>) -> Vec<i64>

shrink folds a list from both ends inwards:

tokens result
[] empty
["a"] a
["a","b"] a+b
["a","b","c"] a(b)c
["a","b","c","d"] a(b+c)d
["a","b","c","d","e"] a(b(c)d)e

That is: with three or more tokens, take the first and the last, and put the recursively-shrunk middle in brackets between them.

windows_sum returns the sum of every consecutive triple. [1,2,3,4] gives [6, 9]; fewer than three elements gives []. Write it without indexing — no v[i], no bounds arithmetic.

Slice patterns turn index code into declarative code

match tokens {
    [] => ...,                        // exactly zero
    [only] => ...,                    // exactly one
    [a, b] => ...,                    // exactly two
    [first, mid @ .., last] => ...,   // two or more, mid may be empty
}

Compare with the version you would otherwise write: a len() check, three branches, tokens[0], tokens[tokens.len() - 1], and a slice expression with two off-by-one opportunities. The pattern version has none of those, cannot panic, and reads as the case analysis it is.

rest @ .. is the @ binding from the previous item applied to a sub-slice: .. matches “any number of elements” and rest @ names them. There may be at most one .. in a slice pattern — otherwise the compiler could not tell where the middle begins.

This is also Rust’s natural answer to head/tail recursion: [first, rest @ ..] is (x:xs) in a language with cons cells, without the cons cells.

The error you will hit first

error[E0529]: expected an array or slice, found `Vec<String>`

Slice patterns match [T; N] and [T]. A Vec<T> is neither; it is a struct that derefs to a slice. Deref coercion happens at method calls and at coercion sites, and a match scrutinee is not one of them. So you must produce a slice yourself:

match v.as_slice() { ... }     // or  match &v[..] { ... }

The starter ships this error because it is the first thing everyone hits. Fixing it properly also changes the shape of the recursion: the helper wants to take &[String], not Vec<String>, so each recursive call is a sub-slice with no allocation at all.

Binding modes, which bit people

Under Rust 2024, matching a &[T] with [first, .., last] binds first and last as &T, not T. That is why the single-element arm needs a .clone() to produce an owned String, and why a + b + c in windows_sum is adding three &i64s (which works — the Add impls cover references).

Half-open ranges inside slice patterns

If you ever nest a range pattern in a slice pattern, parenthesise it: [(1..), ..]. Without the parentheses the parser cannot tell your .. apart from the slice’s ...

A note on indexing

clippy::indexing_slicing (allow-by-default, restriction) flags every v[i] and &v[a..b] as a potential panic. It is deliberately not on here — it would reject far too much ordinary code — but the reason it exists is exactly the reason to prefer slice patterns: an index can be out of bounds, and a pattern cannot. (clippy::match_on_vec_items used to cover the Vec-specific case and has been removed; clippy’s own removal note says indexing_slicing covers it.)

Remember the grade is compile + tests + clippy -D warnings.

Loading visualization…