`trait Generator` vs `trait FnPin`

trait Generator as in nightly:

trait Generator {
    type Yield;
    type Return;
    fn resume(
        self: Pin<&mut Self>
    ) -> GeneratorState<Yield, Return>;
}

There has been discussion of somehow providing resume arguments for generators, which could be useful for e.g. providing a future’s Waker without TLS.

A theoretical FnPin:

trait FnPin<Args> {
    type Output;
    extern "rust-call" fn call_pin(
        self: Pin<&mut Self>,
        args: Args
    ) -> Output;
}

If all we care about is generators as used by the compiler to implement futures, it doesn’t really matter what backs it up.

It feels to me like FnPin is more “pure”, though, as it can be used for any !Unpin-requiring FnMut-style computation, not just that which has the Yield/Return/implementation-defined-on-exhausted semantics. Generator is then just a FnPin<Output=GeneratorResult<Yield, Return>>.

Of course, this can’t join the existing Fn hierarchy, as a pinned reference is incompatible with FnMut and FnOnce "calling convention"s, and Fn implies the other two.

This is mostly just rambling. I’m not sure which is really the better option, if there even is one. I suspect for proper language integration, even if we have FnPin for the general case, we’ll want Generator: FnPin as a “specialization”.

8 Likes

Such a generalization/unification seems very Rust-like. Of course “the devil is in the details”

3 Likes

Do you have more ideas about integrating non-generator closure syntax with FnPin? I have wanted to be able to do pin-projection from a closure into its environment previously, but I can’t remember all the details right now.

2 Likes

The only thing I can think of right now is that a FnPin would be able to use pin_utils::pin_mut!-style “stack pinning” where doing so in a FnMut would be unsound impossible. (Side note, this means that pin_mut! unfortunately should be unsafe to use :slightly_frowning_face:.)

I’m unsure how FnPin construction could be exposed to the surface language, though.

Do you have an example of unsoundness it can cause? It is unusable inside FnMut/Fn and as far as I’m aware sound to use inside FnOnce.

Oh, I didn’t realize that the FnMut restrictions actually prevented it. That’s two potential soundness holes that I thought pin_mut! could be attacked by that the initial move protects against.

(The other one being using macro_rules! to refer to a shadowed name.)

(Edited the incorrect information.)