`FnMut()` syntactic sugar and higher-ranked trait bounds outside of lifetimes

I've got a trait which looks something like this:

pub trait Command {
    fn execute<W: World>(&self, world: &mut W);
}

pub trait World {
    ...
}

Note: The World trait can't be made object safe.

Where the Command should be able to work with anything that implements World (kinda like std::iter::FromIterator and it's iter argument).

Now seeing as this trait only has a single method I'd like to implement it for closures so users don't need to write a bunch of boilerplate structs just to implement a simple Command.

The naive implementation won't work because of unconstrained type parameters and reusing the W type variable.

impl<F, W> Command for F
where
    F: Fn(&mut W),
{
    fn execute<W: World>(&self, target: &mut W) { self(target); }
}

errors with the following:

error[E0403]: the name `W` is already used for a generic parameter in this item's generic parameters
  --> src/commands/mod.rs:11:16
   |
6  | impl<F, W> Command for F
   |         - first use of `W`
...
11 |     fn execute<W: World>(&self, target: &mut W) { self(target); }
   |                ^ already used

error[E0207]: the type parameter `W` is not constrained by the impl trait, self type, or predicates
 --> src/commands/mod.rs:6:9
  |
6 | impl<F, W> Command for F
  |         ^ unconstrained type parameter

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0207, E0403.
For more information about an error, try `rustc --explain E0207`.
error: could not compile `arcs`.

So I tried something akin to the for<'a> syntax used with higher-ranked lifetimes.

impl<F> Command for F
where
    F: for<P: World> Fn(&mut P),
{
    fn execute<W: World>(&self, target: &mut W) { self(target); }
}

But considering HRTBs are only implemented for lifetimes the compiler (rightfully) complains...

error: only lifetime parameters can be used in this context
 --> src/commands/mod.rs:8:12
  |
8 |     F: for<P: World> Fn(&mut P),
  |      

Can anyone think of a formulation which would let me implement Command for a closure type?

I think the best you can do is this:

impl<F> Command for F
where
    F: Fn(&mut dyn World),
{
    fn execute<W: World>(&self, target: &mut W) { self(target); }
}

playground

I had a feeling that would be the case.

Unfortunately my World isn't object safe (it's generics all the way down :rofl:) so I'm not sure if that'll work for me.

Bad luck. I think you'll have to create your own closure struct and implement Command for that manually then.

1 Like

This is not possible as there are no higher-kinded closures in Rust.

What you can do is make a closure accept a trait-object for World, because then you don't have higher-kinded-types (as dyn World erases types).

impl<F> Command for F
where
    F: Fn(&mut dyn World),
{
    fn execute<W: World>(&self, target: &mut W) {
        self(target);
    }
}

Of course, this would require World to be object-safe. If it's not object-safe, you can make a variation of trait that is actually object-safe or put where Self: Sized for non-object-safe-methods. How exactly would you do it depends on the exact reason why World is not object-safe, without knowing the definition of World, I won't be able to tell how you could make it object-safe.

Or you know, forget about closures, and simply require implementing Command for structs. Makes things much easier.

Agreed.

I think I'll drop the World trait and use a single concrete struct. I can't make the trait object safe because the main implementor is a 3rd party type (specs::World) and some fundamental aspects of its API use generics and lifetimes.

In this case I only need to work with one implementor of World and was making it generic so we can use the real World during normal code and a mock during testing.

I'm actually trying to make an undo/redo mechanism on top of specs and was hoping I could move all the complexity into the library so implementing undoable Commands is as easy as possible for end users.

Can your code be rewritten so that the <W> genericity be moved into the Command trait itself ?

trait Command<W : World> {
    fn execute (self: &'_ Self, world: &'_ mut W);
}

impl<W, F : Fn(&'_ mut W)> Command<W> for F {
    #[inline]
    fn execute (self: &'_ F, world: &'_ mut W)
    {
        self(world)
    }
}

How about moving the where clause into the function signature?

impl<T> Command for T {
    fn execute<W: World>(&self, target: &mut W) 
        where Self: Fn(&mut W) {
        /**/
    }
}

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