Skip to content

Functions/closures do not coerce to trait objects and give confusing diagnostics #87093

Open
@ghost

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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-coercionsArea: implicit and explicit `expr as Type` coercionsA-diagnosticsArea: Messages for errors, warnings, and lintsA-dyn-traitArea: trait objects, vtable layoutD-confusingDiagnostics: Confusing error or lint that should be reworked.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions