-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Description
Code
#![feature(type_alias_impl_trait)]
use std::fmt::Display;
pub type Tait<'a> = impl Display;
#[define_opaque(Tait)]
fn define(s: &str) -> Tait<'_> {
s
}
trait Trait {
async fn method(&self, tait: Tait<'_>);
async fn method_2<'t>(&self, tait: Tait<'t>);
async fn this_one_works<'t: 't>(&self, tait: Tait<'t>);
fn this_also_works(&self, tait: Tait<'_>)
-> impl Future<Output = ()> + use<'_, Self>;
}
Current output
error[E0581]: return type references an anonymous lifetime, which is not constrained by the fn input types
--> src/lib.rs:11:5
|
11 | async fn method(&self, tait: Tait<'_>);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: lifetimes appearing in an associated or opaque type are not considered constrained
= note: consider introducing a named lifetime parameter
error[E0581]: return type references lifetime `'t`, which is not constrained by the fn input types
--> src/lib.rs:12:5
|
12 | async fn method_2<'t>(&self, tait: Tait<'t>);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For more information about this error, try `rustc --explain E0581`.
Desired output
error[E0581]: return type captures an anonymous late-bound lifetime, which is not constrained by the fn input types
--> src/lib.rs:11:5
|
11 | async fn method(&self, tait: Tait<'_>);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---^^^
| |
| the unconstrained lifetime
|
= note: lifetimes appearing in an associated or opaque types such as `Tait<'_>` are not considered constrained
= note: `async fn` return a Future which captures all input lifetimes
= help: consider introducing a named lifetime parameter with a bound to make it early-bound (see E0794 to read more about late and early bound parameters)
11 | async fn method<'t: 't>(&self, tait: Tait<'t>);
= help: considering using `-> impl Future + use<..>`, though this may change the semantics of the method
11 | fn method(&self, tait: Tait<'_>) -> impl Future<Output = ()> + use<'_, Self>;
error[E0581]: return type captures late-bound lifetime `'t`, which is not constrained by the fn input types
--> src/lib.rs:12:5
|
12 | async fn method_2<'t>(&self, tait: Tait<'t>);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---^^^
| |
| the unconstrained lifetime
= note: lifetimes appearing in an associated or opaque types such as `Tait<'t>` are not considered constrained
= note: `async fn` return a Future which captures all input lifetimes
= help: add a bound to `'t` make it early-bound (see E0794 to read more about late and early bound parameters)
12 | async fn method_2<'t: 't>(&self, tait: Tait<'t>);
= help: considering using `-> impl Future + use<..>`, though this may change the semantics of the method
12 | fn method_2<'t>(&self, tait: Tait<'t>) -> impl Future<Output = ()> + use<'_, Self>;
Rationale and extra context
This issue is very related to #135350 and the issues linked to it, but doesn't require a separate implementation.
First considering method
: The error talks about the return type, but the return type here is invisible. The entire function is underlined (if this was an RPITIT, the return would be underlined). The on-screen error mainly talks about "constrained", but almost no-one knows what that means. The --explain
text says
In a fn type, a lifetime appears only in the return type and not in the arguments types.
But the lifetime does appear in the argument type. It is true that the first note tries to compensate for this over-generalization, but you have to know what an opaque type is. So in the end, there is a lot that the programmer has to put together to figure out what the compiler is complaining about. (There's a hidden return type here, it captures the input lifetimes, those lifetimes have to be "constrained", showing up in the arguments isn't enough to be constrained if an opaque type is involved, the type Tait<'_> = impl ...
is such an opaque type.)
The second note says
note: consider introducing a named lifetime parameter
That takes us to method_2
. The main error is the same, but now both notes have disappeared. If you happened to start from here, you may not have all the pieces to figure out what the compiler is complaining about. Whether you started here or not, the compiler hasn't given you the actual fix: make the input lifetime early bound.
Late versus early bound is also arcane knowledge, but if it was mentioned, those with enough experience would get a better clue, and those without the arcane knowledge would have a better chance of finding the solution via searching for "early bound".
Better yet would be a structured suggestion like the <'t: 't>
I suggested above.
Alternatively precise capturing (and foregoing async fn
) can remove the capturing from the return type, but this also changes the semantics of the method.
Using a generic instead of a TAIT may be an option sometimes (but also changes the semantics of the method, may effect dyn
compatibility, etc).
Other cases
Rust Version
Playground:
- Build using the Nightly version: 1.90.0-nightly (2025-07-23 ace633090349fc5075b5)
Anything else?
Inspired by an URLO thread. (Although I recognize it's arcane knowledge, I would have solved that thread a lot sooner if the error had just mentioned late/early bound.)
The note:
= note: lifetimes appearing in an associated or opaque type are not considered constrained
implies there may be a way to trigger this use case on stable (without type_alias_impl_trait
) using associated types, but I haven't reproduced such an example so far.