-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tracking Issue for core::pin::pin!
#93178
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 |
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: