Crate fix_hidden_lifetime_bug
source ·Expand description
§::fix_hidden_lifetime_bug
§Are you getting one of the following errors (E700)?
-
ⓘ
error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds --> examples/main.rs:13:40 | 13 | fn foo<'a, 'b>(it: &'a mut &'b ()) -> impl 'a + Sized { | ^^^^^^^^^^^^^^^ | note: hidden type `&'a mut &'b ()` captures the lifetime `'b` as defined on the function body at 13:12 --> examples/main.rs:13:12 | 13 | fn foo<'a, 'b>(it: &'a mut &'b ()) -> impl 'a + Sized { | ^^
Problematic code
ⓘfn foo<'a, 'b>(it: &'a mut &'b ()) -> impl 'a + Sized { it }
-
ⓘ
error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds --> examples/main.rs:8:45 | 8 | async fn bar<'a> (_: &(), _: Box<dyn Send>) { | ^ | note: hidden type `impl Future` captures lifetime smaller than the function body --> examples/main.rs:8:45 | 8 | async fn bar<'a> (_: &(), _: Box<dyn Send>) { | ^
-
ⓘ
error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds --> examples/main.rs:4:57 | 4 | async fn baz<'a>(a: &'static (), b: &(), c: &()) { | ^ | note: hidden type `impl Future` captures lifetime smaller than the function body --> examples/main.rs:4:57 | 4 | async fn baz<'a>(a: &'static (), b: &(), c: &()) { | ^
Problematic code
ⓘasync fn baz<'a>(a: &'static (), b: &(), c: &()) { /* … */ }
EDIT: Fixed as of 1.69.0
Then you can add the attribute provided by this crate to automagically generate an equivalent signature that soothes such a grumpy compiler 🙃
-
See the lifetime bug
async
issue, as well as this other comment for more info.The fix is thus to perform the unsugaring from an
async fn
to anfn
yielding aFuture
, and then just adding the necessary+ Captures<'_>
bounds. -
See also this post where I explain the issue more in detail.
§Usage
-
cargo add fix_hidden_lifetime_bug
, or add the following to yourCargo.toml
file:[dependencies] fix-hidden-lifetime-bug = "x.y.z"
- where you can find the version using
cargo search fix_hidden_lifetime_bug
- where you can find the version using
-
Add the following to your
lib.rs
file:ⓘ#[macro_use] extern crate fix_hidden_lifetime_bug;
-
Slap a
#[fix_hidden_lifetime_bug]
on the problematic function:ⓘ#[fix_hidden_lifetime_bug] // <-- Add this! fn foo<'a, 'b>(it: &'a mut &'b ()) -> impl 'a + Sized { it }
ⓘ#[fix_hidden_lifetime_bug] // <-- Add this! async fn baz<'a>(fst: &'static str, snd: &str, thrd: &str) { /* … */ }
§Extra features
§Full support for methods
In the case of methods, the
Self
type may be hiding lifetime parameters on its own, in which case a macro annotation on the method alone may not have enough syntactical information to generate the fix:ⓘuse ::fix_hidden_lifetime_bug::fix_hidden_lifetime_bug; struct Invariant<'lt> ( fn(&()) -> &mut &'lt (), ); impl Invariant<'_> { #[fix_hidden_lifetime_bug] fn quux(&self) -> impl '_ + Sized { self } }
In that case, the fix is to also decorate the whole
impl
block with the attribute:use ::fix_hidden_lifetime_bug::fix_hidden_lifetime_bug; struct Invariant<'lt> ( fn(&()) -> &mut &'lt (), ); #[fix_hidden_lifetime_bug] impl Invariant<'_> { #[fix_hidden_lifetime_bug] fn quux(&self) -> impl '_ + Sized { self } }
§Displaying the expansions
By enabling the
"showme"
Cargo feature:[dependencies] fix-hidden-lifetime-bug.version = "x.y.z" fix-hidden-lifetime-bug.features = ["showme"]
you can then feed a
showme
parameter to specific#[fix_hidden_lifetime_bug]
annotations, as follows:ⓘ#[fix_hidden_lifetime_bug(showme)]
Example
ⓘuse ::fix_hidden_lifetime_bug::fix_hidden_lifetime_bug; #[fix_hidden_lifetime_bug(showme)] async fn baz<'a>(a: &'static (), b: &'_ (), c: &'_ ()) { println!("Hello, World!"); }
outputs:
fn baz<'a, '_0, '_1, '__async_fut>( a: &'static (), b: &'_0 (), c: &'_1 (), ) -> impl '__async_fut + ::fix_hidden_lifetime_bug::core::future::Future<Output = ()> + ::fix_hidden_lifetime_bug::Captures<'a> + ::fix_hidden_lifetime_bug::Captures<'_0> + ::fix_hidden_lifetime_bug::Captures<'_1> where &'static (): '__async_fut, &'_0 (): '__async_fut, &'_1 (): '__async_fut, { async move { "Mention the input vars so that they get captured by the Future"; let (a, b, c) = (a, b, c); println!("Hello, World!"); } }
This will make the attribute display the result of its expansion (and its expansion only! Hence yielding output that is way more readable than that from
cargo expand
or other such tools), basically showing you how to manually fix a given function signature if you so wish (e.g., to avoid depending on proc-macro processing every time the annotated function is compiled, or to make the life easier for IDEs).Should you fix the signature, you may then be interested in:
§Opting out of the magic proc-macro attribute
If you don’t want to have to recompile each time the proc-macro able to fix function signatures for you (e.g., you rather want it to show you how to fix the signature so that you can do it through exclusive usage of
+ Captures<'…>
additions), so as not to have to pay the proc-macro compilation time each time you compile from scratch, then you can opt out of it by disabling thedefault-features
of the crate: this will disable theproc-macros
features, which is the one that brings it to the table.That way, you can still use this then very lightweight crate just for its
Captures<'…>
(and maybeMentionsTy<…>
) definitions, and the documentation that goes with it![dependencies] … fix-hidden-lifetime-bug.version = "x.y.z" fix-hidden-lifetime-bug.default-features = false
Traits§
- The main hack allowing to mention extra lifetime parameters in an
impl Trait
type without expressing an outlives relationship. - Same as
Captures
, but taking a type parameter instead.
Attribute Macros§
feature = “proc-macros”
See the crate docs for more info.