Implementing traits for function types

I haven’t been able to find any documentation of how trait resolution is supposed to work for functions. The current state in the compiler seems to be that the unique type of a function is not coerced to its fn(args...) -> retval form. So if I have

impl Foo for fn(Bar) -> u32 {
    fn foo(self) { ... }
}

fn baz(bar: Bar) -> u32 {
    ...
}

This works:

Foo::foo(baz as fn(Bar) -> u32);

but this doesn’t:

Foo::foo(baz);

Is this a deliberate feature or has it just not been considered before?

My concrete use case is this: https://play.rust-lang.org/?gist=cad50431d1662d8bf8692c7f17f7f3e7, which AFAICT could be used to implement a requested feature for serde in a very neat, backwards-compatible way.

1 Like

The unique type of a function is coercible to the corresponding fn pointer. But coercions are not performed to match generic bounds. What you want is this, but it results in conflicting implementations. There is an idea for solving this in the far future called intersection impls. So if the cast is not acceptable, you’ll have to find some other design that solves your problem.

I know, I tried that already. It would also be interesting to know if there is a possibility for those two impls not to be considered conflicting in a future version of specialization, but I haven't looked into the details of that yet.

Yeah, but why not, in this case? You can't implement traits for a unique function type, and even if you could I don't really see why you'd want to, so why doesn't trait resolution consider the regular fn type?

Say, you'd have another blanket impl<T> Foo for T and your other impl could not apply any more. Not saying that this is a particularly sensible thing to do in your case, but the general rules must be able to handle that appropriately. I could imagine people doing specializations on trait objects or function pointers for optimization purposes, because they have different run-time characteristics.

Yeah, but why not, in this case? You can’t implement traits for a unique function type, and even if you could I don’t really see why you’d want to, so why doesn’t trait resolution consider the regular fn type?

You make a good point but see here an API evolution that is backwards compatible today but would be backwards incompatible if the coercion happened.

There are a lot of cases where a new version of Rust has changed things in a theoretically-backwards-incompatible way, as long as those changes didn’t have a noticable practical impact. Even adding a function fn1 to a trait T1 is a backwards-incompatible change, because if a type Foo that implements T1 also implements another trait T2 with a function fn1, Foo::fn1(...) becomes ambiguous.

So I’m pretty sure that backwards-compatiblity shouldn’t be a big concern here.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.