Search code examples
ruststreamfuturerust-pin

How to get Pin<&mut T> out of Pin<Arc<Mutex<T>>>


I'm writing a stream adapter that requires a shared ownership of the stream.

Is it correct to store the stream (privately) in Pin<Arc<Mutex<impl Stream>>>?

How can I call poll_next(self: Pin<&mut Stream> on the stream?


Solution

  • Mutex does not have what is called structural pinning, which is described in the std::pin module documentation:

    It turns out that it is actually up to the author of the data structure to decide whether the pinned projection for a particular field turns Pin<&mut Struct> into Pin<&mut Field> or &mut Field. [...] As the author of a data structure you get to decide for each field whether pinning "propagates" to this field or not. Pinning that propagates is also called "structural", because it follows the structure of the type.

    Mutex<T> does not have structural pinning because the pinned-ness of the struct (the Mutex) does not propagate to the field (the T) -- it is safe to get an unpinned &mut T from a Pin<&mut Mutex<T>> (i.e., by using Deref instead of DerefMut and locking the mutex), even if T and Mutex<T> are !Unpin.

    In order to turn this &mut T into a Pin<&mut T>, you have to make another layer of unchecked guarantee, using unsafe and Pin::new_unchecked, to prove that the T itself is never moved. This seems to defeat the purpose of the "outer" Pin.

    Depending on what your data structures allow, you might consider one of these options:

    • writing a wrapper for Arc<Mutex> that pins the contents of the Mutex when accessed through a Pin (you couldn't implement Deref, but you could write methods that returned MutexGuards and did the Pin-wrapping internally)
    • simply pushing the shared mutability inside the type that implements Stream, if your data structure allows it.