
Description
Bad case:
pub trait Foo {}
impl Foo for fn() {}
pub fn bar() {}
const FOOBAR: &'static dyn Foo = &bar;
error[E0277]: the trait bound `fn() {bar}: Foo` is not satisfied
--> bug.rs:5:34
|
5 | const FOOBAR: &'static dyn Foo = &bar;
| ^^^^ the trait `Foo` is not implemented for `fn() {bar}`
|
= note: required for the cast to the object type `dyn Foo`
Worse case:
pub trait Foo {}
impl Foo for fn() {}
const FOOBAR: &'static dyn Foo = &||{};
error[E0277]: the trait bound `[[email protected]:5:35: 5:39]: Foo` is not satisfied
--> bug.rs:5:34
|
5 | const FOOBAR: &'static dyn Foo = &||{};
| ^^^^^ the trait `Foo` is not implemented for `[[email protected]:5:35: 5:39]`
|
= note: required for the cast to the object type `dyn Foo`
In all cases, the workaround is to cast to the function type, for example &(||{} as fn())
. But this is not obvious, not hinted by the diagnostic, and it is surprising that it is required. Moreover, it can in one case produce an even worse diagnostic:
error[E0308]: mismatched types
--> bug.rs:5:34
|
5 | const FOOBAR: &'static dyn Foo = &(bar as fn(_));
| ^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected trait `Foo`
found trait `Foo`
It would be most convenient if no casting would be needed. If casting is unavoidable however, the diagnostic could at least hint this.
These are minimal cases with minimally complicated types. In practice the problem will be less clear. Consider for example that the type signature shown in the diagnostic may include implied lifetime details. It is then easy to mistakenly assume that the lifetimes are the problem.
A practical use of this coercion is making type-safe arrays of pointers to functions with diverse signatures. In such a context it can be especially annoying to have to write each signature twice (in the function/closure definition and in the cast).