[HN Gopher] Pinning in Plain English
       ___________________________________________________________________
        
       Pinning in Plain English
        
       Author : lukastyrychtr
       Score  : 24 points
       Date   : 2021-11-27 12:01 UTC (10 hours ago)
        
 (HTM) web link (blog.schichler.dev)
 (TXT) w3m dump (blog.schichler.dev)
        
       | 2bitencryption wrote:
       | Not to detract from the author's work - it does express the
       | concept in "plain English" as promised - but boy oh boy is
       | Pinning a confusing concept. This is coming from someone who has
       | used rust pretty regularly since 2015.
       | 
       | Sure, in simplest terms, I get it - "Pinning is used to guarantee
       | something is never moved", and the utility of this is mostly
       | (exclusively?) for guarantees in concurrency.
       | 
       | The moment I see the symbol "!Unpin" my brain goes into a fog.
       | "Pinning means things cannot be moved... but most things are
       | Unpin... which can be unpinned after being pinned... but some
       | things are !Unpin... so they can NOT be UN-pinned... so when they
       | are Pinnned they are REALLY pinned..."
       | 
       | It seems to me like "Pin" is a perfectly sound, logical concept,
       | but it was designed by type-system-science nerds, that barely
       | translates to the promise of Rust which is to empower _everyone_
       | , not just type-system-science nerds, to make safe software.
        
         | vvanders wrote:
         | I would largely agree, in the cases where I've needed something
         | pinned I just drop to Box'ing to, keeping the box internal and
         | managing the pinning directly. It's annoying because you lose
         | some classes of things that you may want to pin(values on stack
         | for short durations) but I found similar complexities in the
         | API.
         | 
         | For instance if you take a pinned Arc(ex: Pin<Arc<T>>) there
         | wasn't a way I could determine to get out the mutable interior
         | pointer(ex: via Arc::get_mut) since those APIs require a
         | specific signature and you would need to un-pin the value to
         | make those calls despite them being by reference(safe from a
         | pinning perspective).
         | 
         | Most of Rust is incredibly well designed but so it's pretty
         | jarring that Pinning is so much harder to use. I've found work-
         | arounds for the cases where I've needed either via boxing or
         | dropping to unsafe but would have been nice if it had worked
         | for my use cases.
        
           | cbarrick wrote:
           | > safe from a pinning perspective
           | 
           | The reason you can't get &mut T from Pin<Arc<T>> is because
           | the mutable reference lets you move the data in safe Rust,
           | e.g. with std::mem::swap.
           | 
           | So getting the &mut T from a Pin<Arc<T>> is _not_ safe from a
           | pinning perspective.
        
             | vvanders wrote:
             | Fair, but feels like that significantly reduces the utility
             | of pinned types then if for certain values you lose the
             | ability to mutate values when the compiler overwise knows
             | that there is a single mutable reference.
        
               | Tamschi wrote:
               | There's a hole or oversight in `Arc`'s API in this
               | regard, annoyingly. The function you're looking for would
               | be something like
               | 
               | pub fn get_mut_pinned(this: &mut Pin<Arc<T>>) ->
               | Option<Pin<&mut T>>
               | 
               | , which for pinning-aware values should give you enough
               | mutability and for `T: Unpin` can give you `Option<&mut
               | T>` through `Option::as_deref_mut`. Unfortunately, I
               | haven't seen any `Arc` implementations that actually
               | provide it, since almost none of the third-party ones are
               | pinning-aware. (My `tiptoe::Arc`'s `get_mut` has this
               | signature, but that's a specialty container with
               | additional requirements for the contained value.)
               | 
               | The same goes for `make_mut_pinned`, that's missing too
               | (but to be fair would be much less useful, since pinning
               | and `Clone` don't mix that well).
        
       | dtolnay wrote:
       | > Pins are very often `Unpin`, but this isn't relevant to their
       | function.
       | 
       | This idea is what I've seen get a lot of people lost, but it's
       | pretty natural. By analogy, a DANGER HIGH VOLTAGE sign doesn't
       | itself need to be dangerous or high voltage in order to be
       | useful. If your DANGER HIGH VOLTAGE sign (the sign) is dangerous
       | or high voltage, you'd probably want a second DANGER HIGH VOLTAGE
       | sign pointing out this fact about the first sign. The same for
       | Pin: if you're wanting the pinned pointer (the pointer itself) to
       | not be Unpin, your pinned pointer would need to be pinned by some
       | second pinned pointer.
        
         | Tamschi wrote:
         | This is why I insist on never calling it "pinned pointer" in
         | the post, even if not explicitly. I'm aware the term is used in
         | the standard library docs (for context: `Pin`'s tagline is "A
         | pinned pointer."), but in my eyes that's a misnomer 99% of the
         | time.
         | 
         | It might be a good idea to split the wording thoroughly and
         | officially (into "pinned" vs. "pin"/"pinning ..."), since this
         | is such a common sticking point and seemingly mixing concepts
         | right now. (I.e. "pinned pointers" are (usually)
         | "`Pin`-wrapped" but not "pinned in place". It only becomes more
         | muddled once you have inherently-pinning or "add-on" pins
         | without `Pin` in their type.)
        
       | sfvisser wrote:
       | Unfortunately this write-up didn't help me at all understanding
       | pinning.
       | 
       | Alomost feels I need to read the article to the end in order to
       | understand the assumptions at the start.
       | 
       | I guess I'm missing some context.
       | 
       | - what is 'trivially moveable'? - why would I want to overload
       | the assignment operator? - what is a move constructor? - why
       | would this make memory safe apis tricky? - what does moving
       | references outside unsafe code mean? - what now we suddenly talk
       | about Pin and Unpin, without explaining them first.
       | 
       | I'm so confused
        
         | Tamschi wrote:
         | "Trivially movable" means anyone can take an instance (that
         | they own) and copy its data (just the plain memory cells) to a
         | new location with a new address, then continue to use that
         | instance at that new location without breaking anything. This
         | is a weaker guarantee than `Copy` because they're not
         | necessarily allowed to use both copies of the data as true
         | instances. They can still delay choosing one, but they can only
         | ever interact with one of them through its API.
         | 
         | I'll see if I can add a link to an explanation there. In Rust
         | it's mostly a base assumption and many other languages (C#,
         | Java, JS...) avoid the topic almost entirely, so they all don't
         | explain it very prominently. C++ is much more explicit about
         | it, but unfortunately calls this property a bunch of different
         | names. (I've seen "trivially relocatable" as general term for
         | it, since a "move" seems to be a very specific action in C++.)
         | 
         | The intro section turned out a bit C++-y, maybe. You can read
         | up on the terms by clicking any underlined text. I'm not too
         | happy about links not being blue, but I can't change it without
         | being in the Hashnode ambassador program. (Not much spare money
         | means my entire web presence is strung together from free or
         | very inexpensive platform services. Maybe I'll post about that
         | in the future.)
         | 
         | You can also skip the "The Problem" section entirely, though.
         | It's not that important overall and serves mostly as on-ramp
         | for users of other languages with manual memory management.
        
         | aliceryhl wrote:
         | Trivially moveable means that you can move the object by simply
         | copying the bytes to a new location, and non-trivially-moveable
         | objects are those that would be incorrect to move in this
         | manner. A move constructor is a C++ concept for non-trivially-
         | moveable types that lets you do something more complicated than
         | just copying the bytes over when you want to move the value to
         | a new location, and creating a move constructor is essentially
         | how you overload the assignment operator in C++.
         | 
         | The classic example of something that is not trivially moveable
         | is something with a pointer into itself. If you just copy the
         | bytes, that pointer still points at the old location, which
         | would not be valid. In C++, you would handle that by having a
         | move constructor update the pointer to point at the new
         | location.
         | 
         | Dealing with non-trivially-moveable types make memory safe APIs
         | tricky because Rust generally guarantees that code with no
         | unsafe block can never result in memory unsafety. This also
         | applies to library design, and anyone designing a library with
         | unsafe blocks in it should design their library to provide the
         | same guarantee -- i.e. that anyone using only the safe methods
         | from the library may not be able to cause memory unsafety, even
         | if they can call methods in the library containing unsafe
         | blocks. This should hold no matter how ridiculous the thing
         | they're doing is, as long as it doesn't require unsafe.
         | 
         | So the reason why this is tricky is that safe Rust permits
         | anyone to perform a "trivial move" of any object they have
         | ownership of. Thus, if you make even the constructor of such an
         | object safe, then safe code could call that constructor, then
         | move the object in an unsound way.
         | 
         | So how do you solve this? Any library that has a non-trivially-
         | moveable type needs some way for the user to promise not to
         | move it using a trivial move. The way they do this is by
         | introducing a Pin type, which is a wrapper around a (typically
         | mutable) reference. The primary constructor for Pin is called
         | new_unchecked, and it is an unsafe method whose safety
         | requirement for calling it is that you never move the object
         | that the reference points at. Thus, any library that sees a
         | reference wrapped in a Pin knows that whoever created it _must_
         | have called an unsafe block, promising to follow the rules for
         | that unsafe method. It is, in a sense, a way to shift the blame
         | if anything goes wrong in an unsafe block in the library --
         | normally the library would be at blame because the library is
         | the only one with an unsafe block, but when using Pin, it is
         | the person who _created_ the Pin object who is at blame for the
         | incorrect use of unsafe -- not the library.
         | 
         | It is worth noting that new_unchecked is not the only way to
         | create a Pin. For example, there's a macro called pin! that
         | uses variable shadowing to prevent the user from accessing the
         | underlying variable, preventing the user from moving the object
         | since they can't access the variable name anymore. This lets
         | safe code create a Pin reference to something the safe code
         | owns, and the unsafe block is inside the definition of the
         | macro.
         | 
         | Regarding moving references outside unsafe code, well that's
         | because if someone gets hold of a mutable reference to a pinned
         | value, then they could call std::mem::swap to perform a trivial
         | move of that value, and this would not involve an unsafe block.
         | Thus, if the library exposes any way of getting such a mutable
         | reference in safe code, then that would be an incorrect unsafe
         | block, as safe code is allowed to do anything it wants, as long
         | as it uses only unsafe.
         | 
         | The Pin wrapper type has methods for unwrapping it and getting
         | back the mutable reference that it is a wrapper around, however
         | they are unsafe, so if anyone did that and then called
         | std::mem::swap, they would be at fault because the one who
         | unwrapped it called an unsafe method without following its
         | rules.
         | 
         | As for Unpin, that's a marker on types that are trivially
         | moveable. The Pin wrapper is essentially a no-op for references
         | to those types and provides no guarantees. There's a safe
         | Pin::new method that can only be used for such types.
         | 
         | It's worth pointing out that in Rust, non-trivially-moveable
         | types must still be trivially moveable _until_ they get pinned.
         | So they typically have an "init" state or something like that
         | which can be safely moved around, but then once you pin it, the
         | object becomes non-trivially-moveable.
        
       ___________________________________________________________________
       (page generated 2021-11-27 23:01 UTC)