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

Provide way to refer to projected type #43

Closed
Nemo157 opened this issue Aug 12, 2019 · 4 comments · Fixed by #46
Closed

Provide way to refer to projected type #43

Nemo157 opened this issue Aug 12, 2019 · 4 comments · Fixed by #46
Labels
A-project-attribute Area: #[project], #[project_ref], and #[project_replace] (note: this was removed in v1.0) C-enhancement Category: A new feature or an improvement for an existing one
Milestone

Comments

@Nemo157
Copy link

Nemo157 commented Aug 12, 2019

A couple of things I've wanted to do is to be able to destructure the projection to avoid using this. all the time in methods, and implement helper methods directly on the projection. As an example this currently works because of a lack of hygiene:

#[pin_project::pin_project]
struct Foo {
    #[pin]
    bar: &'static str,
}

impl __FooProjection<'_> {
    fn debug(&self) {
        println!("{:?}", self.bar);
    }
}

fn main() {
    let mut foo = Box::pin(Foo { bar: "baz" });
    foo.as_mut().project().debug();
    let __FooProjection { bar } = foo.as_mut().project();
    println!("{}", bar);
}

but it would be nice to have a way to do this that doesn't rely on the internal details of the macro.

@Nemo157
Copy link
Author

Nemo157 commented Aug 12, 2019

I briefly experimented with a couple of ideas, the first was an inherent associated type:

impl Foo {
    type Projection = __FooProjection;
}

that's currently unsupported (rust-lang/rust#8995) and would presumably require GATs.

The other idea was to add a trait that had an associated type, which would need GATs as well.

My experiments with GAT workarounds seem to indicate that you can't use associated types as the base type in an impl block (impl Foo::Projection<'_> { ... }) or the constructor type in a destructuring statement (let Foo::Projection { bar } = ...;) anyway, so neither of those methods would work even if declaring the associated type worked.

@Nemo157
Copy link
Author

Nemo157 commented Aug 12, 2019

Another horrible idea I had was

mod Foo {
    pub(super) type Projection<'a> = super::__FooProjection<'a>;
}

luckily modules and structs inhabit the same namespace so this is not possible.

@taiki-e
Copy link
Owner

taiki-e commented Aug 12, 2019

I am thinking about two ways.
The first is to use an attribute too impl Foo and convert Foo to __FooProjection.

#[pin_project::project_impl]
impl Foo {
    fn debug(&self) {}
}

// convert to..
impl<'__pin> __FooProjection<'__pin> {
    fn debug(&self) {}
}

This way is almost the same as what the #[project] attribute currently does for let and match.

Another way is to allow naming the projected-type via the #[pin_project] attribute's argument.

#[pin_project::pin_project(projection = Bar)]
struct Foo {
    #[pin]
    bar: &'static str,
}

impl Bar<'_> {
    fn debug(&self) {}
}

We need to document that new lifetimes will be added, but this may be the best solution for this issue and rust-lang/pin-utils#21 at this time.

@taiki-e taiki-e added this to the v0.4 milestone Aug 12, 2019
@Nemo157
Copy link
Author

Nemo157 commented Aug 12, 2019

Oh yeah, I forgot about the #[project] attribute, I've been meaning to take a look at what it actually does. So that already covers the latter usecase, and it would make sense to do something similar for both.

@bors bors bot closed this as completed in #46 Aug 23, 2019
@bors bors bot closed this as completed in b5bc62b Aug 23, 2019
@taiki-e taiki-e added C-enhancement Category: A new feature or an improvement for an existing one A-project-attribute Area: #[project], #[project_ref], and #[project_replace] (note: this was removed in v1.0) labels Sep 23, 2019
bors bot added a commit that referenced this issue May 18, 2020
202: Allow naming the projected types r=taiki-e a=taiki-e

By passing an argument with the same name as the method to the attribute, you can name the projection type returned from the method:

```rust
#[pin_project(
    Replace,
    project = StructProj,
    project_ref = StructProjRef,
    project_replace = StructProjOwn,
)]
struct Struct<T> {
    #[pin]
    field: T,
}

let mut x = Struct { field: 0 };
let StructProj { field } = Pin::new(&mut x).project();
let StructProjRef { field } = Pin::new(&x).project_ref();
let StructProjOwn { field } = Pin::new(&mut x).project_replace(Struct { field: 1 });
```

cc #43
Closes #124

TODO: 
  * [x] add tests
  * [x] write docs 
  * [x] separate unrelated changes; done in #214
  * [x] msrv (`unrestricted_attribute_tokens` requires 1.34); done in #219 


Co-authored-by: Taiki Endo <te316e89@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-project-attribute Area: #[project], #[project_ref], and #[project_replace] (note: this was removed in v1.0) C-enhancement Category: A new feature or an improvement for an existing one
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants