RFA-509 · Case file with fixtures · Case 481 of 694 · Compiler evidence
Rust Generic Arguments Must Match the Item's Parameter List
A generic item defines the number and kinds of arguments accepted by its path. Supply that exact shape, account for defaults, or use the different type whose contract actually matches the intended data.
- 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
- Generic arguments instantiate the resolved item's declared lifetime, type, and const parameter list rather than acting as an open collection of hints.
- First discriminating check
- Resolve the exact item or alias, align arguments with its parameter kinds and defaults, and confirm removing an argument does not discard intended modelling.
I declared Envelope<T> but used Envelope<u8, u16>. Rust emitted E0107 because the type path supplies two generic arguments to an item that declares one parameter.
The failing fixture is simple, yet this error becomes less obvious with type aliases, methods, lifetimes, const parameters, and dependency APIs that changed between versions.
The declaration defines the generic shape
A generic item lists type, const, and lifetime parameters. Uses of that item must supply an accepted argument list, either explicitly or through inference and defaults.
The official E0107 page shows too few and too many arguments for structs and functions. The compiler reports the expected count and the supplied count.
I begin at the resolved declaration rather than guessing which angle-bracket entry to remove.
The repaired fixture supplies one type
The repaired fixture uses Envelope<u8> and constructs the matching tuple struct. This is right when the envelope contains exactly one payload type.
If I intended a key and value, deleting u16 would lose the model. I might need a different type such as Envelope<(u8, u16)> or a redesigned Envelope<K, V>. The diagnostic identifies an arity mismatch, not the intended data shape.
I check the role of every argument before changing the list.
Defaults affect what may be omitted
Some generic parameters have defaults. Callers may omit trailing defaulted arguments in supported positions, while supplying too many remains invalid.
A default does not create a variadic tail. It provides one defined argument when that parameter is omitted. I inspect documentation for the exact item because familiar standard types and third-party types use different defaults.
Inference can also fill arguments from context, but explicit turbofish syntax must still match the declaration's generic structure.
Lifetimes, types, and consts are different kinds
Buffer<'a, T, const N: usize> has three parameters with different roles. A lifetime argument cannot replace a type argument, and an integer const belongs in the const position.
Compiler wording may focus on one kind's count. I write the parameter list and supplied list on separate lines, then align them by kind and order. This is quicker than treating the angle brackets as an untyped list.
The Reference on generics defines these parameter forms.
Methods may have item-level and method-level generics
A type such as Parser<T> can expose parse<U>(). T belongs to the type path or may be inferred from the receiver; U belongs to the method call. Supplying both after the method name can cause E0107.
I inspect which item each ::<> applies to. Fully qualified syntax can make the separation visible when method resolution and inference obscure it.
This is related to, but different from, a trait implementation whose generic method declaration has the wrong parameter count.
Dependency version drift changes counts
A crate may add a defaulted parameter, remove one, or replace a type parameter with an associated type. Code copied from another release can then fail under the resolved version.
I confirm the lockfile version, enabled features, and re-exported type alias. A documentation page for “latest” may not match the compiler's selected declaration.
For migrations, I read why the API changed so I preserve semantics instead of just satisfying arity.
Type aliases can reorder or fix parameters
An alias may expose fewer arguments than the underlying type because it fixes some of them, or it may present them in a different order. I instantiate the alias according to the alias declaration, not according to the implementation type I happen to know underneath.
This is useful for simplifying a public API, but it also means error searches should include the alias definition. Expanding the underlying type mentally can explain semantics; copying its full generic list into the alias use can create E0107. The nearest declared item owns the accepted argument list.
My E0107 checklist
- Which exact item does the generic path resolve to?
- How many lifetime, type, and const parameters does it declare?
- Which arguments are inferred or have defaults?
- Did I supply a method generic in the type's argument list?
- Does each argument's role match the parameter at that position?
- Would removing one argument discard intended data modelling?
- Did the dependency version change the generic API?
- Would a tuple payload or a different type express the intention better?
The core principle is that angle brackets instantiate a declared parameter list; they are not an open collection of type hints. I align every argument with its owner and semantic role before repairing the count.