Compiling playground v0.0.1 (/playground)
error[E0310]: the parameter type `T` may not live long enough
--> src/main.rs:15:32
|
13 | fn gen<T:Foo>() {
| -- help: consider adding an explicit lifetime bound `T: 'static`...
14 | //let func : &dyn Fn() -> i32 = &T::foo; // works
15 | let func : &FooFunc<i32> = &T::foo; // doesn't work, but should be identical to the line above (???)
| ^^^^^^^
|
note: ...so that the type `fn() -> i32 {<T as Foo>::foo}` will meet its required lifetime bounds
--> src/main.rs:15:32
|
15 | let func : &FooFunc<i32> = &T::foo; // doesn't work, but should be identical to the line above (???)
| ^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0310`.
error: Could not compile `playground`.
To learn more, run the command again with --verbose.
All trait objects have a lifetime. dyn Fn -> R is actually just syntax sugar for dyn Fn -> R + 'a, similar to how &T is sugar for &'a T. However, the way that this lifetime 'a is chosen differs based on context:
In a function body, typically all missing lifetime variables are effectively assigned fresh inference variables. This means that:
let func : &dyn Fn() -> i32 = &T::foo;
// is effectively the same as
let func : &'_ dyn Fn() -> i32 + '_ = &T::foo;
However, type inference and lifetime inference only exists inside function bodies. It does not exist in anything that could constitute a public interface, like a type or a function's signature. So omitting a lifetime argument in places such as these always means something else (if it is even allowed).
I'm a bit surprised that the type definition even compiles. But, given the fact that it does compile, it's pretty clear what the compiler has done:
type FooFunc<R> = dyn Fn() -> R;
// must surely be equivalent to
type FooFunc<R> = dyn Fn() -> R + 'static;
Something does seem odd to me. Foo::foo should indeed be 'static, as should T::foo even for unknown T. However, this has nothing to do with whether or not it takes &self; it has to do with the fact that it has no context to close over.
It's a pointer to a function in static memory, just like any expression of the form path::fn_name or path::Type::fn_name. (or <path::Type as path::Trait>::fn_name, etc.)
error[E0310]: the parameter type `T` may not live long enough
--> src/lib.rs:9:5
|
7 | fn foo<T : Trait> ()
| -- help: consider adding an explicit lifetime bound `T: 'static`...
8 | {
9 | is_static(&T::foo);
| ^^^^^^^^^
|
note: ...so that the type `fn() {<T as Trait>::foo}` will meet its required lifetime bounds
--> src/lib.rs:9:5
|
9 | is_static(&T::foo);
| ^^^^^^^^^
In other words: even if T::foo must be 'static no matter T, the compiler still sees T::foo as only outliving what T outlives...
By the way, the same happens with &bar::<T> given fn bar<T>() {}.
This looks like a bug; but it kind of makes sense when we come to think of it: the zero-sized item representing bar<T> (or <T as Trait>::foo) is ill-formed when T is, such as when being outside T's lifetime. So an explicit coercion to the function pointer type (i.e., no longer zero-sized type-specific item, but a dynamic / runtime version of it, with type erasure and thus 'static lifetime) is needed to achieve 'staticness.