# Generic Field Projection

**URL:** <https://internals.rust-lang.org/t/generic-field-projection/16204>\
**Category:** language design\
**Created:** [February 23, 2022, 6:11pm UTC](https://internals.rust-lang.org/t/generic-field-projection/16204 "2022-02-23T18:11:20Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![lachlansneff](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/lachlansneff/32/5924_2.png) [@lachlansneff](https://internals.rust-lang.org/u/lachlansneff)\
**Post date:** [February 23, 2022, 6:11pm UTC](https://internals.rust-lang.org/t/generic-field-projection/16204/1 "2022-02-23T18:11:20Z")

</div>

I found a way to add field projection as a library without requiring a derive macro like previous methods.

For context, field projection basically lets you do things like this:

```rust
struct Foo {
    a: i32,
}

fn project(p: Pin<&Foo>) -> Pin<&i32> {
    p.a // or something like this
}

```

What I’ve figured out is that with a trait, `Project`, it’s possible to add projection to basically any wrapping struct. `Pin` is an obvious one. `MaybeUninit` also makes sense.

Here’s the trait:

```rust
pub trait Project {
    type Base: ?Sized;
    type Output<'a, Field: 'a> where Self: 'a;
    
    unsafe fn project<'a, Field>(&'a self, project_field: fn(*const Self::Base) -> *const Field) -> Self::Output<'a, Field>;
}

```

To use it, you wrap your projections in a macro, `proj!`, which is defined as this:

```rust
macro_rules! proj {
    ($input:ident.$field:ident) => {{
        unsafe {
            <_ as $crate::Project>::project(&$input, |base| unsafe { core::ptr::addr_of!((*base).$field) })
        }
    }};
    (mut $input:ident.$field:ident) => {{
        unsafe {
            <_ as $crate::ProjectMut>::project_mut(&mut $input, |base| unsafe { core::ptr::addr_of_mut!((*base).$field) })
        }
    }};
}

```

Then, you can use this like so:

```rust
struct Foo {
    a: i32,
}

fn project(p: Pin<&Foo>) -> Pin<&i32> {
    proj!(p.a)
}

```

I’d love to see field projection eventually make it’s way into the language (with better syntax than a macro, of course).

I’ve published a crate for this so people can play around with it: [`field-project`](https://crates.io/crates/field-project).

---

<div class="post-metadata">

**Author:** ![Nemo157](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nemo157/32/11585_2.png) [@Nemo157](https://internals.rust-lang.org/u/Nemo157)\
**Post date:** [February 23, 2022, 6:28pm UTC](https://internals.rust-lang.org/t/generic-field-projection/16204/2 "2022-02-23T18:28:51Z")

</div>

Unfortunately that macro is unsound in (at least) the presence of `Drop` implementations:

```rust
use field_project::proj;
use futures_test::future::{AssertUnmoved, FutureTestExt};
use futures::future::{Ready, ready};

struct Foo {
    field: Option<AssertUnmoved<Ready<()>>>,
}

impl Drop for Foo {
    fn drop(&mut self) {
        self.field.take();
    }
}

fn main() {
    futures::executor::block_on(async {
        let foo = Foo {
            field: Some(ready(()).assert_unmoved()),
        };
        futures::pin_mut!(foo);
        proj!(mut foo.field).as_pin_mut().unwrap().await;
    });
}

```

```rust
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `0x7ffdf841efc0`,
 right: `0x7ffdf841ec30`: AssertUnmoved moved before drop', crates.io/futures-test-0.3.21/src/assert_unmoved.rs:171:13

```

---

<div class="post-metadata">

**Author:** ![lachlansneff](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/lachlansneff/32/5924_2.png) [@lachlansneff](https://internals.rust-lang.org/u/lachlansneff)\
**Post date:** [February 23, 2022, 6:33pm UTC](https://internals.rust-lang.org/t/generic-field-projection/16204/3 "2022-02-23T18:33:54Z")

</div>

Ah interesting! Mapping a pin to a field is usually safe—is the edge-case `Drop` implementations?

I think the brute-force way of fixing this is would be to require `Unpin` on the `ProjectMut` implementation for `Pin`. Maybe there's a more elegant way to solve it?

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [February 23, 2022, 7:04pm UTC](https://internals.rust-lang.org/t/generic-field-projection/16204/4 "2022-02-23T19:04:50Z")

</div>

Yeah, the problem here is that `Drop` breaks the promises of `Pin` and provides `&mut Self` rather than `Pin<&mut Self>`.

There's thus two concessions to make this not just immediately unsound:

- If `self` is `mem::swap`ped, and this is unsound, it is inside the privacy safety barrier of the `unsafe` code which relies on the pinning. (Any code outside the privacy barrier cannot possibly observe the move; the generic guarantee ends when `Drop::drop` is called, and the value can no longer be observed by outside code since it's been dropped.)
- The type is in charge of determining if its fields are transitively pinned. Even with `Pin<&mut Self>`, the default, safe option is that the fields themselves are not pinned (they are only pinned once you create `Pin<&mut Field>`). Once `unsafe` is used to project the pin, this promises that `Drop::drop` doesn't violate the pinning guarantee of that field.

So, for `Pin` specifically, the type does have to be aware of pin projection. However, for the `UnsafeCell` family of types, I believe this outside-world projection is sound (though am not 100% confident). This would work well for e.g. `Cell` projection.

---

<div class="post-metadata">

**Author:** ![lachlansneff](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/lachlansneff/32/5924_2.png) [@lachlansneff](https://internals.rust-lang.org/u/lachlansneff)\
**Post date:** [February 23, 2022, 7:28pm UTC](https://internals.rust-lang.org/t/generic-field-projection/16204/5 "2022-02-23T19:28:17Z")

</div>

So, projecting `Pin<&mut T>` is a no go? Does that include `Unpin` types?

---

<div class="post-metadata">

**Author:** ![ckaran](https://avatars.discourse-cdn.com/v4/letter/c/f475e1/32.png) [@ckaran](https://internals.rust-lang.org/u/ckaran)\
**Post date:** [February 23, 2022, 9:02pm UTC](https://internals.rust-lang.org/t/generic-field-projection/16204/6 "2022-02-23T21:02:21Z")

</div>

Take a look at [this project](https://github.com/RustyYato/generic-field-projection) by @RustyYato. He did a great job with it, it just needs documentation (sadly, even though I _should_ be helping with it, every moment I have is taken up by other things).

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [February 23, 2022, 11:00pm UTC](https://internals.rust-lang.org/t/generic-field-projection/16204/7 "2022-02-23T23:00:58Z")

</div>

Obviously it doesn't impact `Unpin` field types, as you can translate between `Pin<&mut T>` and `&mut T` freely.

This is also important to note that an `Unpin` type containing a `!Unpin` field also makes outside pin projection trivially unsound:

```rust
#[derive(Default)]
struct Bomb {
    pinned: PhantomPinned,
}

impl Unpin for Bomb {}

let mut bomb = Bomb::default();

{
    let pin = Pin::new(&mut bomb);
    let pinned: Pin<&mut PhantomPinned> = proj!(mut pin.pinned);
}

let oops: &mut PhantomPinned = &mut bomb.pinned;

```

The pin\_project crate emits guards to prevent incorrect impls of `Drop` or `Unpin` from making pin projection unsound.

---

<div class="post-metadata">

**Author:** ![lachlansneff](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/lachlansneff/32/5924_2.png) [@lachlansneff](https://internals.rust-lang.org/u/lachlansneff)\
**Post date:** [February 28, 2022, 7:05pm UTC](https://internals.rust-lang.org/t/generic-field-projection/16204/8 "2022-02-28T19:05:20Z")

</div>

Hey, thanks for the advice here!

It looks like this won't be as flexible as it could've been otherwise, but it is possible to put bounds on the `Field` type so that it's `Unpin`.

```rust
impl<P, Field: ?Sized> ProjectMut<Field> for Pin<P> where P: DerefMut, Field: Unpin {
    type OutputMut<'a> where Self: 'a, Field: 'a = Pin<&'a mut Field>;

    unsafe fn project_mut<'a>(&'a mut self, project_field: fn(*mut Self::Base) -> *mut Field) -> Pin<&'a mut Field> {
        unsafe {
            self.as_mut().map_unchecked_mut(|base| &mut *project_field(base))
        }
    }
}

```

I'm not sure how useful a pin projection that's only for `Unpin` is, but I think that's safe.

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [May 29, 2022, 7:06pm UTC](https://internals.rust-lang.org/t/generic-field-projection/16204/9 "2022-05-29T19:06:14Z")

</div>

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