# \[Pre-RFC\] \`field\_projection\`

**URL:** <https://internals.rust-lang.org/t/pre-rfc-field-projection/17383>\
**Category:** language design\
**Created:** [September 13, 2022, 8:21pm UTC](https://internals.rust-lang.org/t/pre-rfc-field-projection/17383 "2022-09-13T20:21:00Z")\
**Posts on this page:** 1\
**Showing post:** 51

<div class="post-metadata">

**Author:** ![pitaj](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/pitaj/32/11262_2.png) [@pitaj](https://internals.rust-lang.org/u/pitaj)\
**Post date:** [September 21, 2022, 6:11pm UTC](https://internals.rust-lang.org/t/pre-rfc-field-projection/17383/51 "2022-09-21T18:11:27Z")

</div>

> [@y86-dev](#):
>
> I would say `pin-project` is a rather successful experiment using attributes.

You have a field attribute with no attribute on the struct (unlike any existing proc macros, including `pin-project`). The name of that attribute is declared somewhere else entirely, even in another crate. How do you prevent or deal with name collisions? Does the attribute have to be imported?

Even if you just consider `#[pin]` as a special thing just for Pin (unrelated to defining the name in `field_projecting`), where else in the Rust language as implemented by the compiler (without external crates) is there precedent for a standalone field attribute like this?

> [@y86-dev](#):
>
> There is no unsoundness here. The author of the struct can _choose_ which fields are structurally pinned (`Pin<&mut Field>`) and which are not (`&mut Field`). The part that is `unsafe`, is to keep the same strategy. You are not allowed to produce both a `Pin<&mut Field>` and `&mut Field`. But that is impossible with this proposal (using only safe code).

If you expose a field that should be pinned as unpinned, then you allow moving out of that field, and potentially invalidating any self-references elsewhere in the struct.

Example:

```rust
#![feature(maybe_uninit_uninit_array)]

use std::pin::Pin;
use std::marker::PhantomPinned;
use std::mem::MaybeUninit;

struct NotUnpin {
    inner: u8,
    _pin: PhantomPinned,
}

struct Unmovable {
    data: [MaybeUninit<NotUnpin>; 16],
    len: usize,
    // pointer into `data` or null
    cursor: *mut NotUnpin,
    _pin: PhantomPinned,
}

impl Unmovable {
    fn new() -> Pin<Box<Self>> {
        Box::pin(Unmovable {
            data: MaybeUninit::uninit_array(),
            len: 0,
            cursor: std::ptr::null_mut(),
            _pin: PhantomPinned,
        })
    }
    
    fn push(self: Pin<&mut Self>, item: u8) {
        assert!(self.len < 16);
        let item = NotUnpin {
            inner: item,
            _pin: PhantomPinned
        };
        
        // Safety: we do not move anything out of the mutable reference
        unsafe {
            let this = self.get_unchecked_mut();
        
            this.data[this.len].write(item);
            this.cursor = this.data[this.len].as_mut_ptr();
            this.len += 1;
        }
    }
    
    fn get_item(self: Pin<&Self>) -> Option<&u8> {
        if self.cursor.is_null() {
            None
        } else {
            Some(unsafe { &(*self.cursor).inner })
        }
    }
}

```

Maybe I'm doing something else wrong here, or have a misunderstanding or how the projection should desugar. But it seems like if you were to forget to mark `data` as `#[pin]`, you could easily invalidate `cursor` with safe code:

```rust
fn main() {
    let mut u = Unmovable::new();
    
    dbg!(u.as_ref().get_item());
    u.as_mut().push(1);
    u.as_mut().push(2);
    dbg!(u.as_ref().get_item());
    
    {
        let mut data: Pin<&mut [MaybeUninit<_>; 16]> =  
        // this is just the desugaring of `&mut u.as_mut().data`
        // if `data` is marked with `#[pin]`
        unsafe { u.as_mut().map_unchecked_mut(|u| &mut u.data) };
        
        // error[E0594]: cannot assign to data in dereference 
        // of `Pin<&mut [MaybeUninit<NotUnpin>; 16]>`
        // *data = MaybeUninit::uninit_array();
    }
    
    let data: &mut [MaybeUninit<_>; 16] =  
        // this is just the desugaring of `&mut u.as_mut().data`
        // if `data` is NOT marked with `#[pin]`
        unsafe { &mut u.as_mut().get_unchecked_mut().data };
    
    *data = MaybeUninit::uninit_array();
    
    // woops now `cursor` points to uninitialized memory
    dbg!(u.as_ref().get_item()); // creates a reference to uninitalized memory
}

```

> [@y86-dev](#):
>
> That is not really necessary, because `#[field_projecting]` are already `#[repr(transparent)]`, so they can only have one field that is not a ZST.
> 
> When dealing with `#[inner_projecting]` one might want to project multiple fields. Also only projecting one variant of an `enum` sounds weird. If I have the other variant is it going to panic?

Yeah, on second thought, there would be no way to exclude certain variants from projection, if they contain `$T`, so the target attribute is unnecessary.

> [@y86-dev](#):
>
> I do not like the noise that gets created from that. I have to list _all_ fields again at the beginning of the struct definition.

Well, if it's not useful to exclude certain fields from projection (I don't know if it is or not), then you'd only need to list the `unwrap` fields.

> [@y86-dev](#):
>
> Additionally, I think that giving the unwrap/include operation more meaningful names based on the container also helps.

I think having some attribute at an entirely different location in the code, possibly in the standard library or another crate, define a special word just for this purpose will just lead to confusion. I'm sure it also makes implementing this in the compiler far more difficult. It's better to just have a standard general word for it.

> [@y86-dev](#):
>
> I do not really like the second approach (adding a separate macro invokation), because it also increases the migration friction for someone coming from `pin-project`.

I don't like the second approach either, and I certainly don't think it's better than using an attribute on the struct instead.

> [@y86-dev](#):
>
> But we need to detect if someone has a pinned field. With `#[unpin]` this does not work, because for pinning, unwrapping is actually the safer default (nothing can go wrong if you never create a `Pin<&mut T>`).

I still don't understand why you can't just treat

```rust
struct RaceFutures<F1, F2> {
    fut1: F1,
    fut2: F2,
    #[unpin]
    first: bool,
}

```

like

```rust
struct RaceFutures<F1, F2> {
    #[pin]
    fut1: F1,
    #[pin]
    fut2: F2,
    first: bool,
}

```

To detect if there's a pinned field, you just check if a given field is not marked with `#[unpin]`. Checking for the absence of `#[unpin]` is the same as checking for `#[pin]` and vice-versa.

---

_[View the full topic](https://internals.rust-lang.org/t/pre-rfc-field-projection/17383)._
