Naming Pin/Anchor/Move


#1

RFC 2349 proposing a new set of APIs to deal with types that cannot be safely moved around - especially generators and async functions. You can read more details of the design in the RFC. This thread is a sidebar to discuss the names of these types.

The names proposed in the RFC were arrived at ‘evolutionarily’ as several different previous APIs converged into the present one. So they’re not necessarily very good.

The relationship between the three items (review of the RFC)

The trait Move is an auto trait. Types which don’t implement Move restrict the functionality of Anchor and Pin. They are designed to guarantee that if a type is not Move, once its in an anchor or a pin it will never move again.

Anchor<T> and Pin<'a, T> are a lot like Box<T> and &'a mut T respectively - in fact, they each are just wrappers around those types. The difference between anchor/pin and box/mut ref is that when the type does not implement Move, Anchor and Pin do not provide APIs that allow you to move that type out of them.

Name length

Its important to consider how these types will be used, and how the length of their name impacts their ergonomics. All of these names want to be short

Anchor and Pin will both likely be used as method receivers. Being short makes this easier (especially Pin, because of its lifetime).

    fn foo(self: Anchor<Self>)
    fn bar(self: Pin<'_, Self>)

Move may also appear in APIs as a bound where it is not extremely relevant, similar to Send and Sync. Being short helps make it easier for readers to focus on the more relevant parts of the bound:

where T: MyRelevantTrait<U> + Move + Send
// vs
where T: MyRelevantTrait<U> + IrrelevantMove + Send

Problems with the current names

  • Mixed metaphors: One thing that’s not great about the current name is that its got a bit of mixed metaphors - we have both anchors and pins.
  • Move is very broad: Move sounds like a very general trait, but it only really controls the behavior of Anchor/Pin.
  • No indication of relationship: Anchor is to Box as Pin is to &mut, but the names don’t make this clear at all.

A proposal to get us started

Here’s an alternative set of names which is still pretty short, but resolves the problems with the current names:

  • Pin<'a, T> => Pin<'a, T> (no change)
  • Anchor<T> => PinBox<T>
  • Move => MovePinned

#2

+1 for your proposal


#3
  • Pin<'a, T> => Tethered <'a, T>
  • Anchor<T> => Tether
  • Move => Untethered

Is what I proposed.

Pros when comparing it to the before situation:

  • you know they belong together…
  • a Tethered<T> for T: Untethered looks fishy
  • with a Tethered<T> for T: !Untethered both ends are tethered and T won’t be able to escape its box.

Cons when comparing to @withoutboats’ proposal:

  • Tethered and Untethered look a bit weird when I compare those names against already existing std traits.
    • may just be me
  • longer
  • too similar?

+1 for the Pin family from me


#4

Anchor and Pin could be Pin and PinRef, or BoxFob and Fob. Move could be Slip, Slippery, or Slick, i.e. words that mean something can move while pinned.

The problem I find with Anchor and Pin is that they’re essentially synonyms, since a pin serves to anchor the pinned object. Either you’re creating a pin to hold the thing, and a pin reference to refer to it by, or you pin something and use a fob to access it, with a BoxFob being a particular kind of Fob that owns the pinned object.


#5

I think Pin and PinBox are excellent names. If we want a shorter name than MovePinned for the trait, how about UnPin? That, in my opinion, while being short, nicely illustrates that the type can be “un-pinned”, or moved out from Pin.


#6

Pin and PinBox look great.


#7

Pinned and PinnedBox read better (which I like) but are longer (which I personally don’t mind).

What do we call Move in this scheme? (MovePinned as @withoutboats wrote?)


#9

Hmm, I sort of like Unpin (though I would capitalize it like so), but it also makes it sound as though you are “canceling” the pin, and yet the Pin value remains intact (it just offers weaker guarantees). OTOH, Unpin definitely feels like a more elegant name than MovePinned.

I don’t find MovePinned particularly clear. It should like you can move the Pin<T> around. If we were going to go for clarity, I think I would prefer MoveWhilePinned.

Compared to that, though, Unpin is looking better and better. =)


#10

What about Detach instead of Move?


#11

I’m also intrigued by Unpin. My only fear is this - will this trait end up showing up in bounds really distant from where the pinning is happening, and so users see something like trait Frobulate: Unpin before learning about Pin, and that name gives you no sense of what it means without the context of Pin.

Still Unpin seems better than MovePinned.


#12

The primary thing that has bothered me about Pin is that if you have a Pin<T>, that means to me that your object is pinned. But in my mind, a Pin<T: Move> was a misnomer because your T wasn’t pinned at all. Was Pin lying?

The alternative mental model I developed for Unpin was essentially what @GolDDranks was getting at, although I didn’t read their comment at first. get_mut() or DerefMut is like unpinning a Pin temporarily, allowing you to mess with what’s beneath it before putting the pin back (ending your borrow).

Think of taking a thumbtack out of a cork board so you can tweak how a flyer looks. For Unpin types, this unpinning is directly supported by the type; you can do this implicitly. You can even swap out the object with another before you put the pin back. For other types, you must be much more careful.

I think we can create this mental model on first contact with the trait’s documentation. It’s always going to be the case that when you see a new trait you don’t recognize, you won’t know what to think of it. If you don’t know what a Pin is, MovePinned and MoveWhilePinned are probably going to look just as cryptic to you as Unpin. And by definition, I don’t know of any object that can move around freely while it’s pinned down, so the longer names may be more confusing after all.


#13

I like this line of thinking, though I don’t yet see the name that brings out this intuition. Still, a helpful metaphor to remember.

UPDATE: I guess Unpin is indeed compatible with it. I see your point now – it’s unpinned, just unpinned temporarily (it’s not that the pin was canceled, just lifted).


#14

Seems more like you can Repin in that case, rather than Unpin.

Edit: Of course, Anchor::into_inner does permanently remove the pinning from a value, so since repinning involves unpinning, Unpin is strictly more general and fits better overall.


#15

I think Pin should be renamed to Pinned.


#16

Why, though? Pin is shorter, neater and easier to pronounce.


#17

I already commented on the RFC why:

It doesn’t matter that the name is shorter if it makes less sense than a different one.


#18

You seem to be referring to an older version of the RFC. In the current version, there is no StackPin proposal, or anything called “pinned”.

I think the name Pin works by analogy to MutexGuard. The existence of a MutexGuard means the mutex is locked, but the guard value isn’t called a Locked. Analogously, then, the Pin serves as evidence that the inner value is pinned.

Also, the word “pinned” describes the inner value, not the outer reference. If a Pin was just a flat wrapper type representing the status of the inner value, naming it after the adjective describing that status would make sense. However, the Pin is a separate reference value, so it makes more sense for its name to be a noun, indicating that it is a thing rather than a status.

As for StackPin, in the event of a stack pinning proposal, I agree that it’s inconsistent. If the existence of a Pin is evidence that a value is pinned, and a StackPin is a reference to a stack value that isn’t pinned yet, then it isn’t itself a pin, making the name wrong. I’d suggest a name like StackHasp. I suppose “hasp” is a bit of a non-obvious name (like my above mentioned “fob” which hasn’t seen any love :wink:), but it fits remarkably well, since a hasp is a fastener that needs to be secured with a pin, and a hasp that hasn’t been pinned isn’t secure yet, which perfectly describes the role of the intermediate value required to make stack pinning work.

I’m not sure about PinBox though. I get that it’s a pin which is also a box, but the name suggests that it’s a box for pins rather than a box which is also a pin. Alternatives would be OwningPin by analogy to OwningRef, or PinningBox. These are of course longer, but I’m not sure length matters as much as for Pin.

Also, PinBox makes me think of PMBOK® Guide, but that’s neither here nor there.


#19

So, BoxPin then?


#20

That works too. It sort of suggests it’s a pin for boxes, but that’s closer to reality than a box for pins. There was also discussion of PinMut, so would that be MutPin through symmetry?

Somewhere along the line a reversed adjective order was adopted, so I feel a bit like I’m reading a heraldic blazon, like “a lion passant guardant or, holding in its dexter paw a pin mutable”. The precedent for it is probably 'Fn' [ 'Mut' | 'Once' | 'Box' ]. Other examples in the standard library are actually describing operations, so the modifiers are adverbs which can be in a trailing position. There’s also a conflicting precedent with RefCell and RefMut, with the adjective order flip-flopping essentially within the same construct.


#21

The benefit of such ordering (Pin first) is that variants of “Pin” are next of each other in an alphabetical order (and makes relating them easier in a reference or index).