RFA-617 · Case file with fixtures · Case 589 of 694 · Compiler evidence
Rust Const-Generic Arrays Cannot Use Length-Sensitive Patterns
Array patterns are length-aware structural patterns. Use a fixed array length or borrow as a slice when runtime length categories are the real requirement.
- 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
- A prefix pattern assumes at least one element while the const-generic function admits every N, including zero, as a distinct array shape.
- First discriminating check
- Use a fixed array for a compile-time length invariant or borrow as a slice for runtime prefix and length classification.
An array type includes its length. A function generic over N accepts a family of different array types, including length zero. The pattern [7, ..] assumes a shape containing at least one element. The failing fixture combines them and receives E0730.
One generic body must type-check for every admitted N
Const generics monomorphise the function for concrete lengths, but Rust type-checks the generic definition under its declared constraints. Stable bounds do not generally express “N is at least one” in a way that makes every length-sensitive pattern valid here.
The official E0730 page gives two repairs: choose a fixed-length array or use a slice. The correct choice depends on whether length is a compile-time invariant or runtime input property.
I do not assume production callers never pass zero unless the type contract actually excludes it.
The repair accepts a slice
The repaired fixture accepts &[u8] and matches [7, ..]. Slice patterns naturally test runtime lengths and fail to match empty input rather than making the function ill formed.
Arrays of every length can coerce to slices, as can vectors. This makes the API more general when it only needs to inspect elements and does not depend on N at the type level.
The Reference describes slice patterns, including fixed elements and rest patterns.
Fixed arrays preserve stronger invariants
If a parser requires exactly a four-byte header, [u8; 4] is excellent. A pattern can destructure all four bytes, and callers must provide that shape. Converting to a slice would move length failure into runtime control flow.
I keep const generic arrays when algorithms work uniformly for any N using iteration or indexing guarded by checks. I switch to slices when the algorithm categorises input by runtime prefix, suffix, or length.
Sometimes an API validates a slice and converts it to a fixed array with TryFrom. This creates a clear boundary between untrusted variable-length input and type-checked fixed-size data.
Patterns are not arithmetic constraints
The rest pattern visually suggests it can handle any remaining length, but the leading element still requires a non-empty shape. Pattern typing needs to know structural compatibility, not solve an arbitrary const inequality.
Rust's const generic reference documents allowed const parameter types and use. More advanced generic const expressions evolve over toolchains, so I pin MSRV before choosing a bound-heavy workaround.
For most input processing, first, split_first, starts_with, or a slice match is simpler and produces explicit absence handling.
Ownership differs between array and slice patterns
Matching an array by value can move non-Copy elements. Matching a borrowed slice binds references or uses match ergonomics. I inspect binding types when adapting a pattern so I do not add clones or moves accidentally.
For bytes, Copy makes this subtlety easy to miss. Generic element types expose it. A function accepting &[T] usually intends borrowed inspection; a fixed [T; N] by value transfers the entire array.
Performance should be measured. Fixed lengths can help optimisation and unrolling, while slices reduce monomorphised code and API duplication. A hot cryptographic block differs from a general protocol scanner.
Preserve the invariant at the boundary
I do not convert every array to a slice immediately. If exactly 32 bytes is a security or protocol invariant, the fixed array deserves to remain in the domain type. I perform variable-shape inspection while parsing, validate the length once, then construct the fixed representation. E0730 is useful because it asks whether the function is classifying arbitrary input or operating on an already validated shape. Mixing both jobs produces generic signatures that look strong while still requiring runtime branches.
My E0730 checklist
- Is the scrutinee an array whose length is a const parameter?
- Does the pattern require a minimum or exact number of elements?
- Is length truly a compile-time invariant or runtime input property?
- Would
&[T],split_first, orstarts_withexpress the operation directly? - Should validated input convert into one fixed array type?
- Are matching and borrowing semantics preserved for non-Copy elements?
- Does an advanced const bound fit the project's stable MSRV?
- Have code size and runtime benefits of fixed lengths been measured?
The core principle is that array patterns describe concrete structural shapes. A const parameter describes a family of shapes. E0730 asks me either to choose one member or move shape testing to a slice where runtime length is explicit.