We still don't understand Pin

So we're not sure how much help we're gonna get out of this, but as we understand it, the basic idea of Pin is to define a contract: "You'll not be moved."

So for example, if we had a hexchat plugin crate, with a Plugin trait (which implies Default) we could let plugin = Box::pin(T::default()); where T: Plugin, and then plugin.init(...), and then the code can assume it won't be moved again.

And that's great! But it feels unhelpful.

We have this crate - which does not use Pin - and like, hypothetically, if we wanted to let plugins take &self into hooks, well, we can't. We believe there are at least 2 issues to this:

  1. We're not sure how we'd actually pass the appropriate lifetime on the self: Pin<&Self> such that the hooks can borrow from it. We've heard this is a hard problem, and that there are talks about 'self because of it, so we're not gonna worry about this part for now.
  2. Drop seems like it would be an issue. Let's talk more about this one.

If we understand correctly, there is no "safe Rust" solution to &self in hooks, because hooks are owned by a Plugin, and if the hooks have a Drop type which refers to (parts of) the Plugin, they could observe references to a (partially) deinitialized value, which is UB.

So, if we understand corretly, the best we could ever do, if we wanted to go this route, is provide a guarantee for unsafe Rust to rely on?

And if so, is that worth the extra effort?

To my very non expert understanding: Pin mostly exists to make async work (faster). It doesn't mean you can't move the target, (you are already not allowed to move out of something that has a reference to it, afterall), it means you are providing a way of asking if you can move the content (with Unpin), eg it doesn't contain any pointers or mutexes or the like that would break if they were moved by the async runtime / complier at runtime. (I could easily be wrong about this!)

Self referencing doesn't really match up with that, at least without complier magic like async functions get. For now, Rc/Arc weak references are probably the least bad option.

The idea of Pin is to allow user-code to promise unsafe code in your crate that the user-code will not move some struct that the user-code owns. If you don't need a promise of exactly that form, then you don't need Pin.

Regarding 'self lifetimes and so on, it's important to understand that Pin exists to make some specific kinds of unsafe code correct. It does not make the code safe. Self-referential structs using Pin will still need unsafe code.

9 Likes

can you provide more direct answers?

Can you tell me more about the value you want to pin? In what way is it self-referential? To what extent is the self-referential data on the heap?

These are the questions. The context is this Plugin trait.

So you want to promise the implementor of the Plugin trait that you will never move their plugin struct? You could do that with Pin, yes.

Then the person who writes a plugin would be allowed to make their plugin self-referential.

Can you answer the questions as they were asked?

Sorry, but I don't understand what the question is well enough to answer it.

1 Like

Hmm, we see...

What's on your mind? How are they confusing you?

(also ignore this, it's for us)

Does this work for you?

For the first question, are both of these statements true:

  1. The only point of Pin<&Self> in Plugin is to promise it won't be moved.
  2. Said promise can only be relied on by unsafe code.

For the second question, is this statement true:

  1. There is worth in refactoring the crate to provide Pin<&Self>, despite the effort it would take.

I know you told me to ignore this, but these are questions I can answer.

Yes.

If the alternative is incorrect unsafe code, then yes. Otherwise probably not.

1 Like

oh! okay!

yeah then that answers those. uh, thanks!(?)

One thing that comes to mind is that although Pin could be used for this, another way to achieve the same thing is to make the methods on the trait unsafe, and write that the plugin may not be moved in the safety requirements section of the trait's documentation.

that would still require refactoring the rest of the crate, in this case.