What does this mean — as in, which lifetime is inferred for A
?
struct A<'a>(&'a ());
trait Foo {}
impl<F: FnOnce(A)> Foo for F {}
This compiles, but attempting to use it fails:
fn test() {
fn use_foo<F: Foo>(_: F) {}
use_foo(|_| {});
}
error: implementation of `FnOnce` is not general enough
--> src/main.rs:15:5
|
15 | use_foo(|_| {});
| ^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
= note: closure with signature `fn(A<'2>)` must implement `FnOnce<(A<'1>,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(A<'2>,)>`, for some specific lifetime `'2`
error[E0308]: mismatched types
--> src/main.rs:15:5
|
15 | use_foo(|_| {});
| ^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected trait `for<'r> FnOnce<(A<'r>,)>`
found trait `FnOnce<(A<'_>,)>`
note: this closure does not fulfill the lifetime requirements
--> src/main.rs:15:13
|
15 | use_foo(|_| {});
| ^^^
note: the lifetime requirement is introduced here
--> src/main.rs:14:19
|
14 | fn use_foo<F: Foo>(_: F) {}
| ^^^
(BTW this error message is much improved in nightly over 1.62.0.)
So, the compiler tells us how to fix it: impl<'a, F: FnOnce(A<'a>)> Foo for F {}
But what does what we wrote above mean, and why is it even legal?
Aside: this is not legal:
trait Bar<B> {}
// error[E0106]: missing lifetime specifier
impl<F: Bar<A>> Foo for F {}
// ^ expected named lifetime parameter
//
// help: consider introducing a named lifetime parameter
// impl<'a, F: Bar<A<'a>>> Foo for F {}
We get a very helpful error at the impl site here, so why not above?
Well, it may be since lifetimes can be inferred in functions:
// legal: lifetime
fn use_A(_: A) {}
fn test2() {
let a = ();
let a = A(&a);
// lifetime is inferred at the call site:
use_A(a);
}
This implies that use_A
has a hidden lifetime parameter, effectively:
fn use_A<'a>(_: A<'a>) {}
So, going back to our initial impl, what does it mean and which lifetime is inferred?
impl<F: FnOnce(A)> Foo for F {}
Going by the error we hit with use_foo
, it does not mean what we might expect, that F: FnOnce(A<'a>)
for all 'a
. The message seems to indicate that 'a
is considered an existential. Is there precedence for using existentials in impls? (I know existentials may be used for the opposite: to prove that two impls conflict with some parametrization.) Or is the mentioned '2
actually bound to something, for example the (unnamable) lifetime of F
?
The point of this post: should impls with implied existential lifetimes like above be deprecated? They are confusing and do not appear to have a valid use-case. The use-case is valid; see below.