Skip to content

Commit 452f943

Browse files
authored
Merge pull request #181 from y86-dev/field-projections
[2025H1] add field projection project goal
2 parents 112874d + 7b59865 commit 452f943

File tree

1 file changed

+232
-0
lines changed

1 file changed

+232
-0
lines changed

src/2025h1/field-projections.md

+232
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
# Field Projections
2+
3+
| Metadata | |
4+
| -------- | ------------------------------------------------------------ |
5+
| Owner(s) | @y86-dev |
6+
| Teams | [lang], [libs], [compiler] |
7+
| Status | Proposed |
8+
9+
## Summary
10+
11+
Finalize the [Field Projections RFC] and implement it for use in nightly.
12+
13+
[Field Projections RFC]: https://github.com/rust-lang/rfcs/pull/3735
14+
15+
## Motivation
16+
17+
Rust programs often make use of custom pointer/reference types (for example `Arc<T>`) instead of
18+
using plain references. In addition, container types are used to add or remove invariants on objects
19+
(for example `MaybeUninit<T>`). These types have significantly worse ergonomics when trying to
20+
operate on fields of the contained types compared to references.
21+
22+
### The status quo
23+
24+
Field projections are a unifying solution to several problems:
25+
- [pin projections],
26+
- ergonomic pointer-to-field access operations for pointer-types (`*const T`, `&mut MaybeUninit<T>`,
27+
`NonNull<T>`, `&UnsafeCell<T>`, etc.),
28+
- projecting custom references and container types.
29+
30+
[Pin projections] have been a constant pain point and this feature solves them elegantly while at
31+
the same time solving a much broader problem space. For example, field projections enable the
32+
ergonomic use of `NonNull<T>` over `*mut T` for accessing fields.
33+
34+
In the following sections, we will cover the basic usage first. And then we will go over the most
35+
complex version that is required for [pin projections] as well as allowing custom projections such
36+
as the abstraction for RCU from the Rust for Linux project.
37+
38+
[pin projections]: https://doc.rust-lang.org/std/pin/index.html#projections-and-structural-pinning
39+
[Pin projections]: https://doc.rust-lang.org/std/pin/index.html#projections-and-structural-pinning
40+
41+
#### Ergonomic Pointer-to-Field Operations
42+
43+
We will use the struct from the RFC's summary as a simple example:
44+
45+
```rust
46+
struct Foo {
47+
bar: i32,
48+
}
49+
```
50+
51+
References and raw pointers already possess pointer-to-field operations. Given a variable `foo: &T`
52+
one can write `&foo.bar` to obtain a `&i32` pointing to the field `bar` of `Foo`. The same can be
53+
done for `foo: *const T`: `&raw (*foo).bar` (although this operation is `unsafe`) and their mutable
54+
versions.
55+
56+
However, the other pointer-like types such as `NonNull<T>`, `&mut MaybeUninit<T>` and
57+
`&UnsafeCell<T>` don't natively support this operation. Of course one can write:
58+
59+
```rust
60+
unsafe fn project(foo: NonNull<Foo>) -> NonNull<i32> {
61+
let foo = foo.as_ptr();
62+
unsafe { NonNull::new_unchecked(&raw mut (*foo).bar) }
63+
}
64+
```
65+
66+
But this is very annoying to use in practice, since the code depends on the name of the field and
67+
can thus not be written using a single generic function. For this reason, many people use raw
68+
pointers even though `NonNull<T>` would be more fitting. The same can be said about `&mut
69+
MaybeUninit<T>`.
70+
71+
Field projection adds a new operator that allows types to provide operations generic over the
72+
fields of structs. For example, one can use the field projections on `MaybeUninit<T>` to safely
73+
initialize `Foo`:
74+
75+
```rust
76+
impl Foo {
77+
fn initialize(this: &mut MaybeUninit<Self>) {
78+
let bar: &mut MaybeUninit<i32> = this->bar;
79+
bar.write(42);
80+
}
81+
}
82+
```
83+
84+
There are a lot of types that can benefit from this operation:
85+
- `NonNull<T>`
86+
- `*const T`, `*mut T`
87+
- `&T`, `&mut T`
88+
- `&Cell<T>`, `&UnsafeCell<T>`
89+
- `&mut MaybeUninit<T>`, `*mut MaybeUninit<T>`
90+
- `cell::Ref<'_, T>`, `cell::RefMut<'_, T>`
91+
- `MappedMutexGuard<T>`, `MappedRwLockReadGuard<T>` and `MappedRwLockWriteGuard<T>`
92+
93+
#### Pin Projections
94+
95+
The examples from the previous section are very simple, since they all follow the pattern `C<T> ->
96+
C<F>` where `C` is the respective generic container type and `F` is a field of `T`.
97+
98+
In order to handle `Pin<&mut T>`, the return type of the field projection operator needs to depend
99+
on the field itself. This is needed in order to be able to project structurally pinned fields from
100+
`Pin<&mut T>` to `Pin<&mut F1>` while simultaneously projecting not structurally pinned fields from
101+
`Pin<&mut T>` to `&mut F2`.
102+
103+
Fields marked with `#[pin]` are structurally pinned field. For example, consider the following
104+
future:
105+
106+
```rust
107+
struct FairRaceFuture<F1, F2> {
108+
#[pin]
109+
fut1: F1,
110+
#[pin]
111+
fut2: F2,
112+
fair: bool,
113+
}
114+
```
115+
116+
One can utilize the following projections when given `fut: Pin<&mut FairRaceFuture<F1, F2>>`:
117+
- `fut->fut1: Pin<&mut F1>`
118+
- `fut->fut2: Pin<&mut F2>`
119+
- `fut->fair: &mut bool`
120+
121+
Using these, one can concisely implement `Future` for `FairRaceFuture`:
122+
123+
```rust
124+
impl<F1: Future, F2: Future<Output = F1::Output>> Future for FairRaceFuture<F1, F2> {
125+
type Output = F1::Output;
126+
127+
fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> {
128+
let fair: &mut bool = self->fair;
129+
*fair = !*fair;
130+
if *fair {
131+
// self->fut1: Pin<&mut F1>
132+
match self->fut1.poll(ctx) {
133+
Poll::Pending => self->fut2.poll(ctx),
134+
Poll::Ready(res) => Poll::Ready(res),
135+
}
136+
} else {
137+
// self->fut2: Pin<&mut F2>
138+
match self->fut2.poll(ctx) {
139+
Poll::Pending => self->fut1.poll(ctx),
140+
Poll::Ready(res) => Poll::Ready(res),
141+
}
142+
}
143+
}
144+
}
145+
```
146+
147+
Without field projection, one would either have to use `unsafe` or reach for a third party library
148+
like [`pin-project`] or [`pin-project-lite`] and then use the provided `project` function.
149+
150+
[`pin-project`]: https://crates.io/crates/pin-project
151+
[`pin-project-lite`]: https://crates.io/crates/pin-project-lite
152+
153+
### The next 6 months
154+
155+
#### Finish and accept the Field Projections RFC
156+
157+
Solve big design questions using lang design meetings:
158+
159+
- figure out the best design for field traits,
160+
- determine if `unsafe` field projections should exist,
161+
- settle on a design for the `Project` trait,
162+
- add support for simultaneous projections.
163+
164+
Bikeshed/solve smaller issues:
165+
166+
- projection operator syntax,
167+
- should naming field types have a native syntax?
168+
- naming of the different types and traits,
169+
- discuss which stdlib types should have field projection.
170+
171+
#### Implement the RFC and Experiment
172+
173+
- implement all of the various details from the RFC
174+
- experiment with field projections in the wild
175+
- iterate over the design using this experimentation
176+
177+
### The "shiny future" we are working towards
178+
179+
The ultimate goal is to have ergonomic field projections available in stable rust. Using it should
180+
feel similar to using field access today.
181+
182+
## Ownership and team asks
183+
184+
**Owner:** @y86-dev
185+
186+
| Subgoal | Owner(s) or team(s) | Notes |
187+
| ---------------------------------------------- | -------------------------- | ----- |
188+
| Accept [Field Projections RFC] | | |
189+
| ↳ Design meeting | ![Team][] [lang] | |
190+
| ↳ RFC decisions | ![Team][] [lang] | |
191+
| Nightly Implementation for Field Projections | | |
192+
| ↳ Implementation | ![Help wanted][], @y86-dev | |
193+
| ↳ Standard reviews | ![Team][] [compiler] | |
194+
195+
### Definitions
196+
197+
Definitions for terms used above:
198+
199+
* *Discussion and moral support* is the lowest level offering, basically committing the team to
200+
nothing but good vibes and general support for this endeavor.
201+
* *Author RFC* and *Implementation* means actually writing the code, document, whatever.
202+
* *Design meeting* means holding a synchronous meeting to review a proposal and provide feedback (no
203+
decision expected).
204+
* *RFC decisions* means reviewing an RFC and deciding whether to accept.
205+
* *Org decisions* means reaching a decision on an organizational or policy matter.
206+
* *Secondary review* of an RFC means that the team is "tangentially" involved in the RFC and should
207+
be expected to briefly review.
208+
* *Stabilizations* means reviewing a stabilization and report and deciding whether to stabilize.
209+
* *Standard reviews* refers to reviews for PRs against the repository; these PRs are not expected to
210+
be unduly large or complicated.
211+
* *Prioritized nominations* refers to prioritized lang-team response to nominated issues, with the
212+
expectation that there will be *some* response from the next weekly triage meeting.
213+
* *Dedicated review* means identifying an individual (or group of individuals) who will review the
214+
changes, as they're expected to require significant context.
215+
* Other kinds of decisions:
216+
* [Lang team experiments](https://lang-team.rust-lang.org/how_to/experiment.html) are used to
217+
add nightly features that do not yet have an RFC. They are limited to trusted contributors and
218+
are used to resolve design details such that an RFC can be written.
219+
* Compiler [Major Change Proposal (MCP)](https://forge.rust-lang.org/compiler/mcp.html) is used
220+
to propose a 'larger than average' change and get feedback from the compiler team.
221+
* Library [API Change Proposal
222+
(ACP)](https://std-dev-guide.rust-lang.org/development/feature-lifecycle.html) describes a
223+
change to the standard library.
224+
225+
## Frequently asked questions
226+
227+
### What do I do with this space?
228+
229+
*This is a good place to elaborate on your reasoning above -- for example, why did you put the
230+
design axioms in the order that you did? It's also a good place to put the answers to any questions
231+
that come up during discussion. The expectation is that this FAQ section will grow as the goal is
232+
discussed and eventually should contain a complete summary of the points raised along the way.*

0 commit comments

Comments
 (0)