Implicit coercion already happens in some cases, but trait resolution doesn't fall back to consider coercions.
This issue shows up in various forms on the issue tracker (some examples #58078 #62385 #62621 #86654 #95863), but I couldn't find a more structured discussion around adding support for this.
The main problem is visible in this example:
trait A {}
impl A for fn() {}
fn a() {}
fn test<T: A>(_: T) {}
fn main() {
test(a); // Fails to compile
test(a as fn()); // Compiles
}
As you can see, there are some workarounds for it, including the explicit cast to as fn()
.
For my use-case, I rely a lot on function pointers, casting them to WebAssembly table indexes and sharing them between the host and guest environments. This is only possible with function pointers, so I can't depend on some workarounds with closures (Fn*
traits). I need to be able to do a fn as usize
and get the pointer.The coercion of non-capturing closure -> fn pointer works already and creates nice ergonomics for me, but because this trait coercion doesn't happen automatically, I can't use traits to create some higher level abstractions. At the moment my code is also full of * as fn(_,_,_,_) -> _
and * as fn(_,_) -> _
casts making it a bit ugly.
The error messages around this can also be confusing:
|
10 | test(a); // Fails to compile
| ---- ^ the trait `A` is not implemented for `fn() {a}`
| |
| required by a bound introduced by this call
|
= help: the trait `A` is implemented for `fn()`
First time I came across this, I didn't understand the significance of the {a} part and it looked like the same type. I would also think that fn() {a}
is a more specific version of fn()
and that this should just work.
I can't think of any drawbacks or ambiguity in adding implicit coercion in this case. It could also be that I'm missing something obvious?
This would significantly improve Rust ergonomics for my use-case and I would love to see it supported.