Callable traits: `impl FnOnce for dyn Trait`?

So according to the latest comments https://github.com/rust-lang/rust/issues/29625, implementing FnOnce/Fn/FnMut for my structs is legit (i.e. I can expect it to stabilize at some point... right?).

Meanwhile, let's try taking things one step further - maybe I can make all implementations of a trait callable:

#![feature(unboxed_closures)]
#![feature(fn_traits)]

trait MyTrait {
    fn do_something (&self) -> std::io::Result<()>;
}

impl FnOnce<()> for dyn MyTrait {
    type Output = Result<()>;
    extern "rust-call" fn call_once (self, args: ()) -> Self::Output {
        self.do_something()
    }
}

Yeah? Well, no:

33 |     extern "rust-call" fn call_once (self, args: ()) -> Result<()> {
   |                                      ^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `Sized` is not implemented for `(dyn my_mod::MyTrait + 'static)`
   = help: unsized fn params are gated as an unstable feature
  • Adding a Sized bound to MyTrait just makes things worse:
the trait 'my_mod::MyTrait' cannot be made into an object
  • Enabling #![feature(unsized_locals)] does nothing.

So, are callable trait objects (and/or making structs callable by implementing traits for them) possible at all?

This is done with impl<T: MyTrait> FnOnce<()> for T, without dyn MyTrait.

But that wouldn't work due to the orphan rules, right?

I hope so, but there's no guarantee. The traits may also change shape until stabilized.


I don't know of blanket implementation that would work, but you can implement for Box<dyn MyTrait> and &dyn MyTrait (and the latter would indirectly allow other containers, albeit not in a consuming manner).

Playground.

@leob is correct, unfortunately I get this error:

56 | impl<T: MyTrait> FnOnce<()> for T {
   |      ^ type parameter `T` must be used as the type parameter for some local type

Wish there was something like PhantomData for this scenario...

@quinedot: it works with the Box. How does heap allocation relate to dispatch though? I was hoping I would be able to directly call the structs that implement the trait, see playground

For that you probably do want FnOnce implemented on the objects, so you don't have to coerce to the trait object. As you noted, the orphan rules prevent you from doing this yourself, but you could add a FnOnce supertrait to MyTrait.

As for the difficulties in the playground, it looks to be more about not seeing the trait through a reference in non-generic context than some special behavior for Box specifically.

Looks like this is the issue for it.