Accepting a function returning an opaque iterator borrowing from the input

The following code works:

struct MyState(Vec<usize>);

fn logic(state: &mut MyState) -> impl Iterator<Item = usize> {
    state.0.iter().cloned()
}

struct Engine<State, Logic> {
    state: State,
    logic: Logic,
}

impl<State, Logic> Engine<State, Logic> {
    fn run<'a, Iter>(&'a mut self)
    where
        Logic: FnMut(&'a mut State) -> Iter,
        Iter: Iterator<Item = usize> + 'a,
    {
        for x in (self.logic)(&mut self.state) {
            println!("got {x}");
        }
    }
}

fn main() {
    let state = MyState(vec![1, 2, 3]);
    let mut engine = Engine { state, logic };
    engine.run();
    engine.run();
}

I'd now like to change it so that Engine::run consumes self rather than taking &mut self but I am struggling to figure it out. Note that:

  1. I want the logic function to return a opaque type. I don't want to Box or dynamic dispatch anything.
  2. The iterator returned in the logic function borrows from the passed in state. (I don't want + use<> on the logic function return type).
  3. The logic function should be able to be called multiple times. (Also doesn't work with the above code).

On mobile so no explanation, but ask if you've got any questions. It might not play nice with inference.

2 Likes

this is not doable with the parenthesis syntax for the FnMut() trait, which forces you to name the ouptut associated type, but you can do it with the desugared syntax (which is unstable), or with a helper trait. below is an example I wrote, you can also use the fn-traits's definition instead:

trait MyFnMut1<Arg> {
    type Output;
    fn call(&mut self, arg: Arg)->Self::Output;
}

impl<Arg, Ret, F> MyFnMut1<Arg> for F where F: FnMut(Arg) -> Ret {
    type Output = Ret;
    fn call(&mut self, arg:Arg) -> Ret {
        self(arg)
    }
}

impl<State, Logic> Engine<State, Logic> {
    fn run<>(mut self)
    where
        Logic: for<'a> MyFnMut1<&'a mut State, Output: Iterator<Item=usize>+'a>,
    {
        for x in self.logic.call(&mut self.state) {
            println!("got {x}");
        }
    }
}

this uses the .call() method, if you want to use the call operator on self.logic as before, you can do it with the trick of adding the (redundent) FnMut() trait back:

impl<State, Logic> Engine<State, Logic> {
    fn run<>(mut self)
    where
        Logic: for<'a> MyFnMut1<&'a mut State, Output: Iterator<Item=usize>+'a>,
        Logic: for<'a> FnMut(&'a mut State) -> <Logic as MyFnMut1<&'a mut State>>::Output,
    {
        for x in (self.logic)(&mut self.state) {
            println!("got {x}");
        }
    }
}
3 Likes

Amazing, thanks a lot! Out of curiosity how does the unstable desugared syntax look like? Is there a tracking issue?

the arguments types are grouped into a tuple, and the output type is an associated type, just like the definition in fn-traits.

I remembered the syntax for Fn-family of traits are meant to be permanently unstable, exposing only the sugar form in the surface syntax. but I can't find where I saw this. maybe it's just my imagination.