Let me give slightly more detail: Iâm unable to actually evaluate the post from the perspective of someone unfamiliar with Pin
(although Iâve never before seen the idea to put collections C
into Pin<_>
, instead of just types that implement Deref
). What I do find remarkable though is the fact that an introduction that's trying to make Pin
more easy to learn still doesnât oversimplify anything. In fact, on some points the post is more accurate than the standard library docs itself, because the std docs point out lots of things simply as âyou must not do this or thatâ while the correct / more accurate description is often âyou must not do this or that, unless itâs your own type thatâs pinned and you know the precise circumstances in which pinning is or isnât necessary for soundnessâ. Exactly your point in âPinning is a matter of perspectiveâ. Similarly, the overview of and references to some use cases of pinning are super important.
Anyways, let me point out a few points that might be improved nonetheless
This is possible because moves in Rust are already pretty explicit once references are involved: The underlying assignment maybe be hidden inside another method, but there is no system that will move heap instances around without being told to do so (unlike for example in C#, where pinning is a runtime operation integrated into the GC API.)
The only exception to this are types that are Copy
, a trait which must be derived explicitly for each type for which implicit trivial copies should be available.
(link)
I donât quite understand what you mean by âThe only exception to this are types that are Copyâ. First of all, it isnât clear what âthisâ is referring to, i.e. Iâm wondering âexception from what exactly?â when reading this sentence. (After all, the last point of the previous sentence was some explanation about GC in C#, so thatâs definitely not the thing being referenced.) And even if I relate this to âthere is no system that will move heap instances around without being told to do soâ, it doesnât make sense. Maybe it relates to âmoves in Rust are already pretty explicitâ â but Copy
doesnât really concern moves... maybe something about âexplicitnessâ is related to Copy
, I donât know.
In fact, the only primitive type that is explicitly !Unpin
is core::marker::PhantomPinned
, a marker you can use as member type to make your custom type !Unpin
in stable Rust.
(link)
In my mind, the term âprimitive typeâ only refers to these types, as well as tuples, arrays, slices, string slices, references, pointers, function pointers, i.e. the ones that you donât have to import (even without the prelude), and the ones with special syntax. One page in the reference even only lists boolean, numbers, char, str, and the never type; and calls the rest of non-user-defined types âbuiltin typesâ, which then would also include function types, closure types, trait object types, and abstract impl Trait
return types.
Maybe instead of talking about âprimitive typesâ you should use âstandard library typesâ, or something like that?
However, as the type of the pinned instance itself does not change, it can remain visible "unpinned" inside the module that implements a pin in the first place.
Pin<_>
hides the normal mutable API only through encapsulation, but can't erase it entirely.
This means that safe code in that module can often move an instance even after it appears pinned to code outside of it, and extra care must be taken to avoid such moves.
(link)
Iâm not entirely sure what kind of example you have in mind here. Itâs certainly the case that when youâre implementing an !Unpin
type and using unsafe
code, then some of your safe code might violate properties necessary for soundness. But thatâs more of a general rule for unsafe
code, right? Quite often, unsafe
implementations of e.g. some data structure relies on encapsulation for soundness, and the effect is that safe code in the same module has the power to violate soundness requirements, too. But maybe youâre saying with pinning, itâs particularly easy to accidentally violate the necessary requirements in safe code because moving values is a common thing to do in Rust and often fairly implicit? Nonetheless, it could be more clear that âextra care must be takenâ only applies when your code also uses unsafe
, since without it you either cannot work with something like Pin<&mut T>
at all, or youâll have to use safe abstractions like pin-project, but in the latter case no extra care must be taken anymore.