Thoughts on Pin

Adrian Taylor
2 min readJun 24, 2024

--

As async Rust becomes more popular, we seem to be in a new wave of concern of Rust’s std::pin::Pin type (specifically, about its teachability, which I think is fair). This has long been a feature of C++ interop, with Pin<&mut T> featuring heavily in cxx bindings.

A colleague of mine has advanced the argument that this is a fundamental problem in Rust. The current Deref and DerefMut traits (and similar patterns) allow most pointer-ish things to be boiled down to two fundamental reference types:

  • &T
  • &mut T

but in this colleague’s view, there are actually three:

  • &T
  • &mut T
  • Pin<&mut T>

Perhaps he’s right, and we’re all doomed, at least without a whole new standard library. (A similar concern was also expressed in the early days of Pin design).

But conversely, perhaps &mut T almost always is pinnable, and we’re overthinking this?

Could we do something clever on an edition boundary?

We already have:

  • The Unpin auto-trait.
  • The PhantomPinned marker, so that folks can manually mark things as moveable or not.

On an edition boundary, could we:

  • Deprecate the current std::mem::{swap, take, replace}
  • Create new equivalents which only accept &mut T where T: Unpin
  • Do not allow pass-by-value of T unless T: Unpin . Obviously, this would be accompanied by excellent diagnostics: You cannot pass thingy by value because it does not implement Unpin. Consider using Box::new(thingy).
  • There are a set of exception cases where the compiler can prove that the data will be constructed in-place and will not move, and thus pass-by-value is allowed, e.g. Box::new . This is C++ prvalue semantics.
  • We simply remove the need for the Pin type, Pin<&mut T> , Box::pinand pin! . A T can just hang out on the stack even if it’s !Unpin ; you just won’t be able to pass it by value.

It might be a bigger compatibility break than can normally be achieved on an edition boundary but it feels like there’s a bit of a groundswell around this.

I’m sure all these permutations have been explored before. Furthermore, my colleague describes all this as “completely impossible” because people may be reliant on the Pin<&mut T> vs &mut T distinction. My take is that relatively few people currently care about immovable data (just those working with futures and us weird C++ interop folks) and virtually nobody will be relying on movability in generics. So it feels like we might be able to chart some sort of course here, with suitably liberal use of crater experiments.

(NB I’m not sure all of this is especially relevant to C++ interop in the long term, since I’ve become convinced that it’s not safe at scale to use any Rust reference to point to C++ data. But Pin is still annoyingly frictiony).

--

--

Adrian Taylor

Ade works on Chrome at Google, and likes mountain biking, climbing, snowboarding, and usually his kids. All opinions are my own.