Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
ghost opened this issue Jul 12, 2021 · 1 comment
Labels
A-coercions Area: implicit and explicit `expr as Type` coercions A-diagnostics Area: Messages for errors, warnings, and lints A-trait-objects Area: trait objects, vtable layout D-confusing Diagnostics: Confusing error or lint that should be reworked. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@ghost
Copy link

ghost commented Jul 12, 2021

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 `[closure@bug.rs: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 `[closure@bug.rs: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).

@ghost ghost added A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jul 12, 2021
@ghost
Copy link
Author

ghost commented Aug 7, 2021

https://doc.rust-lang.org/std/primitive.fn.html#creating-function-pointers

When bar is the name of a function, then the expression bar is not a function pointer. Rather, it denotes a value of an unnameable type that uniquely identifies the function bar. The value is zero-sized because the type already identifies the function. This has the advantage that “calling” the value (it implements the Fn* traits) does not require dynamic dispatch.

If &dyn always requires dynamic dispatch, maybe it is okay to coerce?

@fmease fmease added D-confusing Diagnostics: Confusing error or lint that should be reworked. A-trait-objects Area: trait objects, vtable layout A-coercions Area: implicit and explicit `expr as Type` coercions labels Dec 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-coercions Area: implicit and explicit `expr as Type` coercions A-diagnostics Area: Messages for errors, warnings, and lints A-trait-objects Area: trait objects, vtable layout D-confusing Diagnostics: Confusing error or lint that should be reworked. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

1 participant