-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Tracking Issue for core::pin::pin!
#93178
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Comments
core::pin::pin!
core::pin::pin!
…ro, r=m-ou-se Add a stack-`pin!`-ning macro to `core::pin`. - rust-lang#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 rust-lang/rfcs#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!`: rust-lang#82913 - [x] Create the tracking issue.
Fix the generator example for `pin!()` The previous generator example is not actually self-referential, since the reference is created after the yield. CC rust-lang#93178 (tracking issue)
I feel like this should be called |
Not sure what you mean, you can give it a |
Ok, after all this time, there have been no issues with The main unresolved question being about the naming of the macro: should it remain as
|
That's why I'd vote for:
|
This all sounds good to me. I'm excited to see this move forward, thank you for pushing on it!
Either way is fine but personally I'd make a PR, less chance of getting lost :) |
It's worth stating that this is a deficiency of Rust's lifetime extension rules (a deficiency that the definition of |
…macro, r=dtolnay Stabilize `::{core,std}::pin::pin!` As discussed [over here](rust-lang#93178 (comment)), it looks like a decent time to stabilize the `pin!` macro. ### Public API ```rust // in module `core::pin` /// API: `fn pin<T>($value: T) -> Pin<&'local mut T>` pub macro pin($value:expr $(,)?) { … } ``` - Tracking issue: rust-lang#93178 (now all this needs is an FCP by the proper team?)
…macro, r=dtolnay Stabilize `::{core,std}::pin::pin!` As discussed [over here](rust-lang#93178 (comment)), it looks like a decent time to stabilize the `pin!` macro. ### Public API ```rust // in module `core::pin` /// API: `fn pin<T>($value: T) -> Pin<&'local mut T>` pub macro pin($value:expr $(,)?) { … } ``` - Tracking issue: rust-lang#93178 (now all this needs is an FCP by the proper team?)
…macro, r=dtolnay Stabilize `::{core,std}::pin::pin!` As discussed [over here](rust-lang#93178 (comment)), it looks like a decent time to stabilize the `pin!` macro. ### Public API ```rust // in module `core::pin` /// API: `fn pin<T>($value: T) -> Pin<&'local mut T>` pub macro pin($value:expr $(,)?) { … } ``` - Tracking issue: rust-lang#93178 (now all this needs is an FCP by the proper team?)
…dtolnay Stabilize `::{core,std}::pin::pin!` As discussed [over here](rust-lang/rust#93178 (comment)), it looks like a decent time to stabilize the `pin!` macro. ### Public API ```rust // in module `core::pin` /// API: `fn pin<T>($value: T) -> Pin<&'local mut T>` pub macro pin($value:expr $(,)?) { … } ``` - Tracking issue: #93178 (now all this needs is an FCP by the proper team?)
For anyone wanting to start using this style of pinning without having to bump their MSRV, below is a workaround. The extra use std::future::Future;
use std::ops::Deref;
use std::pin::Pin;
use std::task::{Context, Poll};
pub struct Pinner<'a, T> {
pub unsafe_pointer: &'a mut T,
}
impl<'a, T> Pinner<'a, T> {
pub fn as_mut(&mut self) -> Pin<&mut T> {
// SAFETY: as long as Pinner is only ever constructed via the pin!()
// macro and the unsafe_pointer field is never directly accessed,
// then the value is safe to pin here. this is because the macro
// ensures the input is turned into a borrowed anonymous temporary,
// preventing any further access to the original value after Pinner
// is constructed, and Pinner has no methods that enable moving out
// of the reference. the word "unsafe" is used in the field name to
// discourage direct access. this is the best we can do, since the
// field must be public for the macro to work.
unsafe { Pin::new_unchecked(self.unsafe_pointer) }
}
pub fn set(&mut self, value: T) {
self.as_mut().set(value)
}
}
impl<'a, T> Pinner<'a, Option<T>> {
pub fn as_pin_mut(&mut self) -> Option<Pin<&mut T>> {
self.as_mut().as_pin_mut()
}
}
impl<'a, T> Deref for Pinner<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.unsafe_pointer
}
}
impl<'a, T> Future for Pinner<'a, T>
where
T: Future,
{
type Output = T::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
T::poll(Pin::into_inner(self).as_mut(), cx)
}
}
#[macro_export]
macro_rules! pin {
($x:expr) => {
crate::Pinner {
unsafe_pointer: &mut { $x },
}
};
} |
Should this one be closed? |
…dtolnay Stabilize `::{core,std}::pin::pin!` As discussed [over here](rust-lang/rust#93178 (comment)), it looks like a decent time to stabilize the `pin!` macro. ### Public API ```rust // in module `core::pin` /// API: `fn pin<T>($value: T) -> Pin<&'local mut T>` pub macro pin($value:expr $(,)?) { … } ``` - Tracking issue: #93178 (now all this needs is an FCP by the proper team?)
Error is coming on this one please check: error[E0658]: use of unstable library feature 'pin_macro'
error[E0658]: use of unstable library feature 'pin_macro' |
@carrycooldude this means the version of rust (toolchain) you are using is too old for that |
Uh oh!
There was an error while loading. Please reload this page.
Feature gate:
#![feature(pin_macro)]
This is a tracking issue for
core::pin::pin!
, which allows pinning values to the stack / local scope.Public API
Steps / History
pin!
-ning macro tocore::pin
. #93176::{core,std}::pin::pin!
#103800(un)Resolved Questions
Should it be named
pin_mut!
instead, and have apin_ref!
counterpart? (cc @cramertj's Add a stack-pin!
-ning macro tocore::pin
. #93176 (comment))I claim this has been deemed resolved by this conclusion
The text was updated successfully, but these errors were encountered: