Skip to content

Tracking Issue for core::pin::pin! #93178

@danielhenrymantilla

Description

@danielhenrymantilla
Contributor

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

// core::pin

/// API: `fn pin<T>($value: T) -> Pin<&'local mut T>`
pub macro pin($value:expr $(,)?) {}

Steps / History

(un)Resolved Questions

Activity

added
C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
T-libs-apiRelevant to the library API team, which will review and decide on the PR/issue.
on Jan 21, 2022
changed the title [-]Tracking Issue for the `core::pin::pin!`[/-] [+]Tracking Issue for `core::pin::pin!`[/+] on Jan 21, 2022
added a commit that references this issue on Feb 15, 2022
added a commit that references this issue on Jun 20, 2022
625c929
jsenkpiel-godaddy

jsenkpiel-godaddy commented on Oct 24, 2022

@jsenkpiel-godaddy

I feel like this should be called pin_mut! and have a pin_ref! counterpart, because you can't feed it T but only &mut T/&T.

RalfJung

RalfJung commented on Oct 25, 2022

@RalfJung
Member

Not sure what you mean, you can give it a T just fine. You will get a Pin<&mut T>, but that can be easily converted into Pin<&T> if needed via as_ref.

danielhenrymantilla

danielhenrymantilla commented on Oct 29, 2022

@danielhenrymantilla
ContributorAuthor

Ok, after all this time, there have been no issues with nightly users of this feature, and the macro seems as good as it is possible to have at the moment.

The main unresolved question being about the naming of the macro: should it remain as pin!, or should it become pin_mut! to pave the way for a pin_ref! counterpart?

While pin_mut! would be a safe / conservative choice, I think it would be overkill here.
To illustrate why, let's start by drawing some parallels with Box or other ownership-based APIs.

This is a legitimate think to do, since pin!ning is the stack/local counterpart of Box::pinning, i.e., its key aspect is ownership-based, not borrowing-based: contrary to the usual getter design of .as_{ref,mut}, .deref{,_mut}, ptr::addr_of{_mut}!, pin! consumes ownership of its input!

And in the same fashion that we have Box::leak() consuming ownership of the input, and returning a borrow in output, a borrow which happens to be an exclusive one, pin!'s design is quite similar.

And we don't have have Box::leak_mut() + Box::leak_ref(), we just just have Box::leak(). Users wanting a shared reference after leaking just have to &* (the idea being that such function returns &mut because it's maximally capable, a shared reference being a strictly less powerful API):

let p: &mut _ = Box::leak(thing); // `thing` **moved out**
let r: &_ = &*p;
let p: Pin<&mut _> = pin!(thing); // `thing` **moved out**
let r: Pin<&_> = p.as_ref();
  • (there is one difference, though: pin! uses subtle lifetime extension1, which doesn't allow for methods to be directly called on the pin!ned thing as it is being assigned to a local. This makes the two lines in the example be required for proper usage, which isn't the case for Box).

On the contrary, if we went with pin_mut!, we'd then have a symmetric pin_ref! / pin_mut! situation, which I actually find not that useful:

That is, there is little motivation in having a symmetric convenience API for a situation which is hugely asymmetric.

Worse, keeping the symmetry will actually end up being confusing, much like having Box::leak_mut() vs. Box::leak_ref() could have been: since the ref variant could be implemented in terms of the mut one, users could legitimately wonder if there was some other difference they might have missed: it would be a case of accidental complexity, as I view it.

  • That is: having both pin_mut! and pin_ref! could be confusing for Alan, should they decide to try to write block_on, select!, or something like that without resorting to Box::pin.

Conclusion

pin_mut! does not seem to pull its weight compared to pin!; we should go with the latter, and ensure a "pit of success" for people such as Alan to use the right tool from the start 🙂, and avoid some bits of accidental complexity.

  • (In the future, should pin_ref! really be deemed useful, we could always imitate the .project() / .project_ref() design of #[pin_project]: pin! and pin_ref!. This would offer the tiny extra flexibility of not requiring two statements for the Pin<&_> case, while still heavily favoring the &mut case, as it should be).

Footnotes

  1. [EDIT] as @tmandry points out, this doesn't even have to remain being an issue in the long term, as finding a way to solve RFC 66 (Better temporary lifetimes (tracking issue for RFC 66) #15023) could help fix this 🤞

  2. Granted, .project() is a potentially more frequent name for other APIs, but from randomly skimming the grep.app page results, I was seeing elements indicative of work on Futures or Pinning.

danielhenrymantilla

danielhenrymantilla commented on Oct 29, 2022

@danielhenrymantilla
ContributorAuthor

That's why I'd vote for:

  • sticking to pin! (no pin_mut!), and should people agree with this, it means it's time for:
  • starting an FCP for its stabilization (should I make a tentative stabilization PR to trigger it, or should I wait for the FCP before making the PR?)
tmandry

tmandry commented on Oct 29, 2022

@tmandry
Member

This all sounds good to me. I'm excited to see this move forward, thank you for pushing on it!

should I make a tentative stabilization PR to trigger it, or should I wait for the FCP to before make the PR?

Either way is fine but personally I'd make a PR, less chance of getting lost :)

tmandry

tmandry commented on Oct 29, 2022

@tmandry
Member
  • (there is one difference, though: pin! uses subtle lifetime extension, which doesn't allow for methods to be directly called on the pin!ned thing as it is being assigned to a local. This makes the two lines in the example be required for proper usage, which isn't the case for Box).

It's worth stating that this is a deficiency of Rust's lifetime extension rules (a deficiency that the definition of pin! itself works around by being in std), and would be fixed when RFC 66 is implemented (#15023).

15 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCT-libs-apiRelevant to the library API team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @RalfJung@jkarneges@ojeda@tmandry@danielhenrymantilla

        Issue actions

          Tracking Issue for `core::pin::pin!` · Issue #93178 · rust-lang/rust