Add a stack-`pin!`-ning macro to `core::pin`.
- https://github.com/rust-lang/rust/issues/93178
`pin!` allows pinning a value to the stack. Thanks to being implemented in the stdlib, which gives access to `macro` macros, and to the private `.pointer` field of the `Pin` wrapper, [it was recently discovered](https://rust-lang.zulipchat.com/#narrow/stream/187312-wg-async-foundations/topic/pin!.20.E2.80.94.20the.20.22definitive.22.20edition.20.28a.20rhs-compatible.20pin-nin.2E.2E.2E/near/268731241) ([archive link](https://zulip-archive.rust-lang.org/stream/187312-wg-async-foundations/topic/A.20rhs-compatible.20pin-ning.20macro.html#268731241)), contrary to popular belief, that it is actually possible to implement and feature such a macro:
```rust
let foo: Pin<&mut PhantomPinned> = pin!(PhantomPinned);
stuff(foo);
```
or, directly:
```rust
stuff(pin!(PhantomPinned));
```
- For context, historically, this used to require one of the two following syntaxes:
- ```rust
let foo = PhantomPinned;
pin!(foo);
stuff(foo);
```
- ```rust
pin! {
let foo = PhantomPinned;
}
stuff(foo);
```
This macro thus allows, for instance, doing things like:
```diff
fn block_on<T>(fut: impl Future<Output = T>) -> T {
// Pin the future so it can be polled.
- let mut fut = Box::pin(fut);
+ let mut fut = pin!(fut);
// Create a new context to be passed to the future.
let t = thread::current();
let waker = Arc::new(ThreadWaker(t)).into();
let mut cx = Context::from_waker(&waker);
// Run the future to completion.
loop {
match fut.as_mut().poll(&mut cx) {
Poll::Ready(res) => return res,
Poll::Pending => thread::park(),
}
}
}
```
- _c.f._, https://doc.rust-lang.org/1.58.1/alloc/task/trait.Wake.html
And so on, and so forth.
I don't think such an API can get better than that, barring full featured language support (`&pin` references or something), so I see no reason not to start experimenting with featuring this in the stdlib already 🙂
- cc `@rust-lang/wg-async-foundations` \[EDIT: this doesn't seem to have pinged anybody 😩, thanks `@yoshuawuyts` for the real ping\]
r? `@joshtriplett`
___
# Docs preview
https://user-images.githubusercontent.com/9920355/150605731-1f45c2eb-c9b0-4ce3-b17f-2784fb75786e.mp4
___
# Implementation
The implementation ends up being dead simple (so much it's embarrassing):
```rust
pub macro pin($value:expr $(,)?) {
Pin { pointer: &mut { $value } }
}
```
_and voilà_!
- The key for it working lies in [the rules governing the scope of anonymous temporaries](https://doc.rust-lang.org/1.58.1/reference/destructors.html#temporary-lifetime-extension).
<details><summary>Comments and context</summary>
This is `Pin::new_unchecked(&mut { $value })`, so, for starters, let's
review such a hypothetical macro (that any user-code could define):
```rust
macro_rules! pin {( $value:expr ) => (
match &mut { $value } { at_value => unsafe { // Do not wrap `$value` in an `unsafe` block.
$crate::pin::Pin::<&mut _>::new_unchecked(at_value)
}}
)}
```
Safety:
- `type P = &mut _`. There are thus no pathological `Deref{,Mut}` impls that would break `Pin`'s invariants.
- `{ $value }` is braced, making it a _block expression_, thus **moving** the given `$value`, and making it _become an **anonymous** temporary_.
By virtue of being anonynomous, it can no longer be accessed, thus preventing any attemps to `mem::replace` it or `mem::forget` it, _etc._
This gives us a `pin!` definition that is sound, and which works, but only in certain scenarios:
- If the `pin!(value)` expression is _directly_ fed to a function call:
`let poll = pin!(fut).poll(cx);`
- If the `pin!(value)` expression is part of a scrutinee:
```rust
match pin!(fut) { pinned_fut => {
pinned_fut.as_mut().poll(...);
pinned_fut.as_mut().poll(...);
}} // <- `fut` is dropped here.
```
Alas, it doesn't work for the more straight-forward use-case: `let` bindings.
```rust
let pinned_fut = pin!(fut); // <- temporary value is freed at the end of this statement
pinned_fut.poll(...) // error[E0716]: temporary value dropped while borrowed
// note: consider using a `let` binding to create a longer lived value
```
- Issues such as this one are the ones motivating https://github.com/rust-lang/rfcs/pull/66
This makes such a macro incredibly unergonomic in practice, and the reason most macros out there had to take the path of being a statement/binding macro (_e.g._, `pin!(future);`) instead of featuring the more intuitive ergonomics of an expression macro.
Luckily, there is a way to avoid the problem. Indeed, the problem stems from the fact that a temporary is dropped at the end of its enclosing statement when it is part of the parameters given to function call, which has precisely been the case with our `Pin::new_unchecked()`!
For instance,
```rust
let p = Pin::new_unchecked(&mut <temporary>);
```
becomes:
```rust
let p = { let mut anon = <temporary>; &mut anon };
```
However, when using a literal braced struct to construct the value, references to temporaries can then be taken. This makes Rust change the lifespan of such temporaries so that they are, instead, dropped _at the end of the enscoping block_.
For instance,
```rust
let p = Pin { pointer: &mut <temporary> };
```
becomes:
```rust
let mut anon = <temporary>;
let p = Pin { pointer: &mut anon };
```
which is *exactly* what we want.
Finally, we don't hit problems _w.r.t._ the privacy of the `pointer` field, or the unqualified `Pin` name, thanks to `decl_macro`s being _fully_ hygienic (`def_site` hygiene).
</details>
___
# TODO
- [x] Add compile-fail tests with attempts to break the `Pin` invariants thanks to the macro (_e.g._, try to access the private `.pointer` field, or see what happens if such a pin is used outside its enscoping scope (borrow error));
- [ ] Follow-up stuff:
- [ ] Try to experiment with adding `pin!` to the prelude: this may require to be handled with some extra care, as it may lead to issues reminiscent of those of `assert_matches!`: https://github.com/rust-lang/rust/issues/82913
- [x] Create the tracking issue.