You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Associated type with a 'static bound still captures lifetimes when using impl Trait. #106684
Using a returned impl Trait for a trait with an associated type and not specifying the associated type leads to the following error:
error[E0700]: hidden type for `impl Trait + 'static` captures lifetime that does not appear in bounds
..even when + 'static is specified, meaning no lifetimes should be captured. Adding #![feature(associated_type_bounds)] and instead using impl Trait<AssocType: 'static> as a return type solves the issue. I'm not sure if this is intended behavior - it seems pretty strange since AssocType is defined with a 'static bound. See the code below for a reproducible example.
#![feature(associated_type_bounds)]traitAnything{}impl<T: ?Sized>AnythingforT{}traitThing{typeOutput:'static;fncalc(&mutself) -> Self::Output;}impl<'a>Thingfor&'au8{typeOutput = u8;fncalc(&mutself) -> u8{**self}}// uncomment the bound to make it workfnget_thing(x:&u8) -> implThing/* <Output: 'static> */ + '_{
x
}fnf(n:&u8) -> implAnything + 'static{letmut t = get_thing(n);
t.calc()}
I am hitting this too. Additionally, adding an associated type bound like impl Trait<AssocType: 'static> doesn't always seem to work. Since the 2024 edition lifetime capture rules are likely to make this even worse, I also tried the proposed solution of type alias impl trait, but it has the same issue.
error[E0700]: hidden type for `impl Dummy` captures lifetime that does not appear in bounds
--> view/src/bug.rs:36:5
|
35 | fn use_impl_rpit(r: &()) -> impl Dummy {
| --- ---------- opaque type defined here
| |
| hidden type `<impl Captures<&()> + AsStatic + 'static as AsStatic>::Static` captures the anonymous lifetime defined here
36 | ret_impl(&r).as_static() // ERROR
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
help: to declare that `impl Dummy` captures `'_`, you can add an explicit `'_` lifetime bound
|
35 | fn use_impl_rpit(r: &()) -> impl Dummy + '_ {
| ++++
error[E0700]: hidden type for `Ret` captures lifetime that does not appear in bounds
--> view/src/bug.rs:41:5
|
39 | type Ret = impl Dummy;
| ---------- opaque type defined here
40 | fn use_impl_tait(r: &()) -> Ret {
| --- hidden type `<impl Captures<&()> + AsStatic + 'static as AsStatic>::Static` captures the anonymous lifetime defined here
41 | ret_impl(&r).as_static() // ERROR
| ^^^^^^^^^^^^^^^^^^^^^^^^
The code from the issue description compiles in the next edition — Rust 2024 — which is not stable yet. That's because the capturing rules for have been adjusted. See rust-lang/rfcs#3498 for details.
I can't reproduce your supposed impl Thing ⟼ impl Thing<Output: 'static> fix.
The lifetime bound 'static does not influence the set of captured generic arguments (which might be obvious by now).
Further note that the code from the issue description can be “fixed” today by making get_thing return impl Thing + Captures<'_>, f return impl Anything + Captures<'_> (and I guess by removing various 'static bounds, taking a cursory glance they don't seem useful, I might be mistaken though) where trait Captures<'a> {} impl<'a, T: ?Sized> Captures<'a> for T {}. Note that impl Thing + '_ & impl Anything + '_ would also “work” but that solution wouldn't super general and breaks down if invariant (and contravariant?) generic arguments are involved.
If you enable the feature precise_capturing, you can turn these two return types into impl use<'_> Thing and impl use<'_> Anything respectively. As alluded to in the first paragraph, Rust 2024 captures all input lifetimes automatically making the two occurrences of use<'_> redundant in this specific scenario.
@kmicklas I'm not sure what you mean by “the 2024 edition lifetime capture rules are likely to make this even worse”. In Rust 2024, use_impl_rpit from your example compiles contrary to Rust 2021 (:P).
In Rust 2021, you can make use_impl_rpit compile using the Captures trick that you seem to be aware of (, “+ '_” if you don't care about the loss of generality) or by writing impl use<'_> Dummy under the feature precise_capturing.
In the majority of cases, we do want to capture all in-scope generic params. The plan is to accept, finalize & stabilize the feature precise_capturing rather soon-ish IINM to give the user precise control over captures. This eliminates the need for the Captures trick and the need to turn an inline impl Trait into a TAIT.
To make your TAIT snippet compile, you need to apply the following change:
- type Ret = impl Dummy;+ type Ret<'a> = impl Dummy;
fn use_impl_tait(r: &()) -> Ret {
Or under #![deny(elided_lifetimes_in_paths)]:
- type Ret = impl Dummy;+ type Ret<'a> = impl Dummy;- fn use_impl_tait(r: &()) -> Ret {+ fn use_impl_tait(r: &()) -> Ret<'_> {
@fmease Maybe example wasn't clear, but explicitly capturing the lifetimes is not a solution because the goal is to have a 'static associated type which does not capture any lifetimes. Thanks for pointing me to the use<> syntax, that addresses the worries I had about the 2024 edition changes.
I've since realized there is a solution that was not obvious to me before - nested use of impl:
This achieves the goal of capturing lifetimes in the top level return type, but not its associated type.
My naive intuition is still that the 'static bound on the associated type declaration itself should have been sufficient, but I think I understand better why it doesn't work like that. (It clearly wouldn't generalize to captured generic types, for example.)
error[E0700]: hidden type for `impl Anything + 'static` captures lifetime that does not appear in bounds
--> src/lib.rs:25:5
|
23 | fn f(n: &u8) -> impl Anything + 'static {
| --- ----------------------- opaque type defined here
| |
| hidden type `<impl Thing + '_ as Thing>::Output` captures the anonymous lifetime defined here
24 | let mut t = get_thing(n);
25 | t.calc()
| ^^^^^^^^
|
help: add a `use<...>` bound to explicitly capture `'_`
|
23 | fn f(n: &u8) -> impl Anything + 'static + use<'_> {
| +++++++++
For more information about this error, try `rustc --explain E0700`.
Activity
PixelDust22 commentedon Mar 30, 2023
Bump. Also ran into this issue.
kmicklas commentedon May 12, 2024
I am hitting this too. Additionally, adding an associated type bound like
impl Trait<AssocType: 'static>doesn't always seem to work. Since the 2024 edition lifetime capture rules are likely to make this even worse, I also tried the proposed solution of type alias impl trait, but it has the same issue.Error
Meta
rustc --versionfmease commentedon May 12, 2024
The code from the issue description compiles in the next edition — Rust 2024 — which is not stable yet. That's because the capturing rules for have been adjusted. See rust-lang/rfcs#3498 for details.
I can't reproduce your supposed
impl Thing⟼impl Thing<Output: 'static>fix.The lifetime bound
'staticdoes not influence the set of captured generic arguments (which might be obvious by now).Further note that the code from the issue description can be “fixed” today by making
get_thingreturnimpl Thing + Captures<'_>,freturnimpl Anything + Captures<'_>(and I guess by removing various'staticbounds, taking a cursory glance they don't seem useful, I might be mistaken though) wheretrait Captures<'a> {} impl<'a, T: ?Sized> Captures<'a> for T {}. Note thatimpl Thing + '_&impl Anything + '_would also “work” but that solution wouldn't super general and breaks down if invariant (and contravariant?) generic arguments are involved.If you enable the feature
precise_capturing, you can turn these two return types intoimpl use<'_> Thingandimpl use<'_> Anythingrespectively. As alluded to in the first paragraph, Rust 2024 captures all input lifetimes automatically making the two occurrences ofuse<'_>redundant in this specific scenario.fmease commentedon May 12, 2024
@kmicklas I'm not sure what you mean by “the 2024 edition lifetime capture rules are likely to make this even worse”. In Rust 2024,
use_impl_rpitfrom your example compiles contrary to Rust 2021 (:P).In Rust 2021, you can make
use_impl_rpitcompile using theCapturestrick that you seem to be aware of (, “+ '_” if you don't care about the loss of generality) or by writingimpl use<'_> Dummyunder the featureprecise_capturing.In the majority of cases, we do want to capture all in-scope generic params. The plan is to accept, finalize & stabilize the feature
precise_capturingrather soon-ish IINM to give the user precise control over captures. This eliminates the need for theCapturestrick and the need to turn an inlineimpl Traitinto a TAIT.To make your TAIT snippet compile, you need to apply the following change:
Or under
#![deny(elided_lifetimes_in_paths)]:kmicklas commentedon May 17, 2024
@fmease Maybe example wasn't clear, but explicitly capturing the lifetimes is not a solution because the goal is to have a
'staticassociated type which does not capture any lifetimes. Thanks for pointing me to theuse<>syntax, that addresses the worries I had about the 2024 edition changes.I've since realized there is a solution that was not obvious to me before - nested use of
impl:This achieves the goal of capturing lifetimes in the top level return type, but not its associated type.
My naive intuition is still that the
'staticbound on the associated type declaration itself should have been sufficient, but I think I understand better why it doesn't work like that. (It clearly wouldn't generalize to captured generic types, for example.)Dylan-DPC commentedon Jan 9, 2025
Current output: