(NOT A CONTRIBUTION)
Comparing overwrite with improving the UX of Pin is really difficult because they're just in different categories of language changes, but "minpin" is directly comparable to "pinned places." There is really only one key difference (every other difference is syntactic and not important) which concerns pin projections.
In the updated form of pinned places, pinned projections are permitted if a type meets these requirements:
- If it implements Unpin, it does so using the auto-trait mechanism, not a manually written impl.
- If it implements Drop, either it implements Unpin or its destructor uses the fn drop(&pin mut self) signature.
Niko replaces the first requirement that either the type being projected to implements Unpin
or the type being projected through has an explicit negative implementation of Unpin
. The aim here is to be more explicit, which is an admirable goal, but it means that strictly fewer pin projections are made safe, and the pin projections that are not made safe are critically important.
Specifically, Niko's rules do not adequately cover combinators, because combinators are Unpin
iff the futures/streams they abstract over are Unpin
. So in the implementation of Future
for Join
, for example, you do not know either that the joined futures are Unpin
(because they may not be) or that Join
is !Unpin
(because it is Unpin
if the joined futures are), and cannot use pin projections with Niko's rules.
Niko "solves" this by adding an explicit negative impl of Unpin
for Join
, but this is unnecessary for correctness and reduces the ways the combinator can be use, for no reason except to satisfy the requirements that Niko has imposed to give the implementer convenience features. Specifically, a Join of two Unpin futures should be moveable after it has been pinned, because it contains no self-referential state.
For future combinators this doesn't matter very much because you're unlikely to move a future after you start polling it, but for stream combinators it actually does: if you pin a stream to call next on it, and it implements Unpin
, you can then move it later. There are plenty of plausible use cases for this, like special handling the head message from a stream and then looping over the remaining messages.
I doubt libraries would willingly make their API surface less useful than it has to be, so I would expect library authors to continue to maintain unsafe pin projection code instead of blanket negative implementing Unpin
for their combinators (also this would be a breaking change). Since combinators are the primary use case for pin projections, this would make the feature far less useful.
The goal of making opting into pin projections explicit seems reasonable, but a different mechanism that does not involve unnecessarily restricting functionality should be found if that requirement is to be met.