Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make generated 'project' reference take an '&mut Pin<&mut Self>' #47

Merged
merged 4 commits into from
Aug 23, 2019

Commits on Aug 23, 2019

  1. Make generated 'project' reference take an '&mut Pin<&mut Self>'

    Based on rust-lang/unsafe-code-guidelines#148 (comment)
    by @CAD97
    
    Currently, the generated 'project' method takes a 'Pin<&mut Self>',
    consuming it. This makes it impossible to use the original Pin<&mut Self>
    after calling project(), since the 'Pin<&mut Self>' has been moved into
    the the 'Project' method.
    
    This makes it impossible to implement useful pattern when working with
    enums:
    
    ```rust
    
    enum Foo {
        Variant1(#[pin] SomeFuture),
        Variant2(OtherType)
    }
    
    fn process(foo: Pin<&mut Foo>) {
        match foo.project() {
            __FooProjection(fut) => {
                fut.poll();
                let new_foo: Foo = ...;
                foo.set(new_foo);
            },
            _ => {}
        }
    }
    ```
    
    This pattern is common when implementing a Future combinator - an inner
    future is polled, and then the containing enum is changed to a new
    variant. However, as soon as 'project()' is called, it becoms imposible
    to call 'set' on the original 'Pin<&mut Self>'.
    
    To support this pattern, this commit changes the 'project' method to
    take a '&mut Pin<&mut Self>'. The projection types works exactly as
    before - however, creating it no longer requires consuming the original
    'Pin<&mut Self>'
    
    Unfortunately, current limitations of Rust prevent us from simply
    modifiying the signature of the 'project' method in the inherent impl
    of the projection type. While using 'Pin<&mut Self>' as a receiver is
    supported on stable rust, using '&mut Pin<&mut Self>' as a receiver
    requires the unstable `#![feature(arbitrary_self_types)]`
    
    For compatibility with stable Rust, we instead dynamically define a new
    trait, '__{Type}ProjectionTrait', where {Type} is the name of the type
    with the `#[pin_project]` attribute.
    
    This trait looks like this:
    
    ```rust
    trait __FooProjectionTrait {
        fn project(&'a mut self) -> __FooProjection<'a>;
    }
    ```
    
    It is then implemented for `Pin<&mut {Type}>`. This allows the `project`
    method to be invoked on `&mut Pin<&mut {Type}>`, which is what we want.
    
    If Generic Associated Types (rust-lang/rust#44265)
    were implemented and stablized, we could use a single trait for all pin
    projections:
    
    ```rust
    trait Projectable {
        type Projection<'a>;
        fn project(&'a mut self) -> Self::Projection<'a>;
    }
    ```
    
    However, Generic Associated Types are not even implemented on nightly
    yet, so we need for generate a new trait per type for the forseeable
    future.
    Aaron1011 committed Aug 23, 2019
    Configuration menu
    Copy the full SHA
    d20f3fe View commit details
    Browse the repository at this point in the history
  2. Run 'cargo fmt'

    Aaron1011 committed Aug 23, 2019
    Configuration menu
    Copy the full SHA
    45b2933 View commit details
    Browse the repository at this point in the history
  3. Fix tests

    Aaron1011 committed Aug 23, 2019
    Configuration menu
    Copy the full SHA
    19e6d69 View commit details
    Browse the repository at this point in the history
  4. Try constructing projection types first

    This ensures that we bail out early if we encounter an error
    Aaron1011 committed Aug 23, 2019
    Configuration menu
    Copy the full SHA
    c3be220 View commit details
    Browse the repository at this point in the history