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.