RFA-593 · Case file with fixtures · Case 565 of 694 · Compiler evidence
Rust Anonymous Lifetime Cannot Be Declared as a Generic Name
Use anonymous lifetime where inference is permitted and a real named lifetime where inputs and output must share a stated relationship.
- 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
- Placeholder syntax intended for an inferred type position was mistaken for a reusable lifetime identity that could connect inputs and output.
- First discriminating check
- Trace borrowed sources and use a real named lifetime for shared relationships, leaving anonymous inference only in positions that permit it.
'_ asks Rust to infer an anonymous lifetime in positions where anonymous lifetimes are allowed. It is not a name that a generic parameter list can declare. The failing fixture writes fn choose<'_> and receives E0637.
Anonymous and named lifetimes solve different problems
A name such as 'a connects multiple type positions. If two inputs and an output use 'a, the signature states that the returned borrow comes from data available under the shared call-scoped relationship.
'_ deliberately avoids giving the inferred region a reusable name. It is useful when a type requires lifetime syntax but no explicit relationship needs to be referenced elsewhere. Declaring it as if it were 'a contradicts that purpose.
The E0637 explanation also covers omitted lifetimes in positions where normal elision is not available, such as certain associated-type constraints. A named lifetime or higher-ranked bound may be required there.
The repair names the real relationship
The repaired fixture introduces 'a and uses it for both inputs and the output. Because choose may return either string, that shared relationship is meaningful and matches the body's data flow.
Simply replacing every '_ with a different fresh name would be wrong if the positions need to connect. I trace the possible source of each returned or stored reference, then use names to state exactly those connections.
Normal function lifetime elision can infer many signatures. One borrowed input can supply an output lifetime automatically. With two borrowed inputs and an output, Rust cannot choose a source without an explicit relationship, so the fixture needs a name even after the illegal declaration is removed.
Associated types often need higher-ranked intent
A bound like Iterator<Item = &u32> has no function-input elision context that establishes whose lifetime the item carries. Depending on the API, I may introduce a lifetime parameter tied to an owner, or require for<'a> behaviour that works for every lifetime.
Those contracts differ substantially. T: Iterator<Item = &'a u32> describes one iterator yielding references valid for a particular 'a. Higher-ranked bounds describe implementations valid across caller-chosen lifetimes, commonly with lending callback shapes rather than ordinary Iterator due to its fixed associated Item type.
I expand aliases and read the trait definition before adding syntax copied from an error example. Lifetime position determines what can actually be expressed.
Placeholder lifetime does not mean static
'_ does not request 'static, and Rust should not infer it as a way to escape owner scope. It means an appropriate lifetime inferred from context. The resulting borrow remains limited by its source.
Likewise, replacing E0637 with 'static often over-constrains the API or falsely suggests globally valid data. I use static only for data and types that genuinely satisfy that contract.
Trait objects have default object lifetime rules that can also differ from ordinary reference elision. Writing dyn Trait + '_ can explicitly tie an object to a surrounding borrow. Again, this is use of a placeholder in a supported type position, not declaration of a parameter named underscore.
Names should communicate roles during review
For complex functions I use diagnostic names such as 'request, 'store, and 'view while developing. Rust accepts longer lifetime names, and they can make outlives bounds readable. I may simplify them when the final signature has one obvious relation.
Compile-fail tests are valuable for APIs whose negative lifetime guarantee matters. Runtime tests cannot demonstrate that a returned view is forbidden from outliving its owner.
My E0637 checklist
- Is
'_being declared in a generic parameter list rather than used as a placeholder? - Which reference positions need the same named relationship?
- Can ordinary function lifetime elision express the contract?
- Does an associated type position require an explicit or higher-ranked lifetime?
- Is
'staticbeing proposed without truly static ownership? - Do trait-object default lifetime rules match the surrounding borrow?
- Would role-based names clarify several outlives relationships?
- Is the signature aligned with every source the body may return?
The core principle is that anonymity means the lifetime has no reusable source-level name. I use '_ when context can infer one isolated role and introduce a real parameter when an API must state how borrowed inputs, outputs, or stored values relate.