RFA-448 · Case file with fixtures · Case 420 of 694 · Compiler evidence
A Rust Slice Pattern Requires an Array or Slice Value
Pattern syntax follows the scrutinee's structural type. Use an array or slice for element destructuring, a tuple for fixed heterogeneous positions, and conversion methods before matching when another type only has an internal sequence-like representation.
- Reviewed
- Rust
- Rust 1.98.1, edition 2024
- Targets
- all Rust targets
- Profiles
- dev, release, test
Direct answer
What this Rust failure means
- Why it happens
- Pattern syntax follows the scrutinee's structural type, and a scalar floating-point value has no array or slice elements for bracket syntax to expose.
- First discriminating check
- Inspect the scrutinee type and choose array, slice, tuple, enum, or conversion operations that reflect its actual public structure.
I used [left, right] in a match whose input was an f32. Rust reported E0529 because bracket patterns can match arrays and slices, not a scalar value.
The failing fixture is intentionally obvious. Real versions are often hidden behind aliases, references, generic associated types, or an assumption that a wrapper “contains two parts.”
Pattern shape must match type shape
A slice pattern describes zero or more ordered elements of an array or slice. A fixed pattern [left, right] requires exactly two elements.
The E0529 explanation says the scrutinee must have a consistent array or slice type. Rust does not reinterpret the bytes of another value merely because bracket syntax was used.
This is a type check before any runtime matching decision.
Use an array when fixed homogeneous positions are the data
The repaired fixture changes the value to [f32; 2] and destructures both elements.
An array encodes its length in the type and stores elements of one type. [left, right] is irrefutable for [f32; 2] because every such array has exactly two positions.
For a slice &[f32], the same fixed-length pattern is refutable because runtime length may differ. It belongs inside match, if-let, or let-else.
Use a tuple for heterogeneous fixed components
If the two parts have different types or distinct roles, (left, right) and a tuple may fit. A named struct is often clearer when role names matter beyond one local calculation.
Choosing an array only to make bracket syntax compile can erase domain distinctions. I select representation first and let the pattern mirror it.
Convert before matching when the API exposes a sequence
A vector can be matched through its slice view:
match values.as_slice() {
[left, right] => { /* exactly two */ }
[first, rest @ ..] => { /* one or more */ }
[] => { /* empty */ }
}
String and scalar values need type-specific conversion. A float can expose bits with to_bits() or bytes with to_ne_bytes(), but matching those bytes answers a representation question, not “destructure a float into two numbers.”
I name conversions so endianness and representation intent remain visible.
Rest patterns define length families
[head, tail @ ..] matches any non-empty slice and binds the remainder as a slice. [.., last] matches any non-empty slice from the other edge. [] matches empty.
These patterns make parser boundaries readable, but I test empty, singleton, exact, and longer inputs. The rest pattern may match zero elements.
Only one .. can appear because two variable remainders would leave their split ambiguous.
References and match ergonomics affect binding types
Matching &[T] can bind references according to match ergonomics and the edition's rules. If code expects owned T values, Copy status or explicit cloning matters.
I inspect inferred types in an editor or add small annotations. A structurally correct pattern can still produce &T where later code expects T.
Mutation requires &mut [T] and non-overlapping bindings. Slice patterns preserve Rust's borrowing guarantees across elements and remainders.
Generic code needs the right associated type
When E0529 appears behind a generic, I inspect the actual scrutinee type after projections. A trait returning Item does not imply that Item is a slice even if every current implementation happens to choose one.
The trait can return &[T], expose AsRef<[T]>, or define the needed operation instead of relying on hidden concrete shapes.
The slice-pattern Reference defines fixed elements, rest patterns, and allowed binding forms.
My E0529 checklist
I also separate a sequence view from an iterator. Slice patterns need a value with indexable contiguous sequence shape; an iterator only promises that values can be produced over time. Collecting an iterator just to use [first, ..] may allocate and may change streaming behaviour. Often next() is the direct operation. Conversely, when a parser already owns a byte slice, a slice pattern can express a short fixed prefix clearly without copying. The choice is not only about satisfying E0529. It should preserve the data source's cost model and whether the remainder must stay borrowed.
- What is the exact scrutinee type after references and aliases?
- Is its structure an array, slice, tuple, struct, or scalar?
- Is length known statically or checked at runtime?
- Would a named type express component roles better?
- Is a conversion exposing semantic elements or raw representation bytes?
- What types do bindings receive through references?
- Are empty and variable-length cases handled?
The core principle is that pattern notation does not coerce arbitrary data into a shape. Brackets destructure array and slice elements. I expose the correct representation explicitly, then use patterns to state valid lengths and bindings without confusing storage bytes with domain structure.