Can Mutex<T> be Pinned

For example, T: !Unpin, so Mutex<T>: !Unpin

but the code can replace T wrapped by Pin because the mutex provides &mut T

let mut x: Pin<&Mutex<T>> = todo!();
let g = x.lock().unwrap();
*g = T::new();

am I wrong?

Mutex<T> is not a pointer thus Pin<Mutex<T>> works precisely like expected… what's the question?

1 Like

Sorry, spell error, consider Pin<&mut Mutex<T>> ---> &Mutex ---> Mutex::lock(&self) ---> MutexGuard ---> &mut T

You couldn't go from Pin<&mut T> or Pin<&T> to T if T is not Unpin (without unsafe)

Means your conversion from Pin<&mut <Mutex<T>> couldn't be converted to &Mutex and the rest doesn't matter.

1 Like

The value inside a mutex is not pinned under current Mutex implementation.

1 Like

Note that Mutex is not special because Pin and Unipin are almost non-special, too.

In praticular the core rule of Rust every type must be ready for it to be blindly memcopied to somewhere else in memory is not affected by Pin, Unpin or Mutex.

That, in turn, means that Mutex<T> can not be pinned: Pin<Mutex<T>> can be easily moved to somewhere else in memory and then what does that even give us?

Compare with Pin<&T> or Pin<Arc<T>>: in these cases, because there are pointers inside, we have something useful: when we move these types in memory they are moved, sure… but the pointee is not moved because there are level of indirection involved… that's how Pin provides some value: it couple to anything to the block of memory that's the entity lives in, but if there are indirection… oh, now we may talk.

But, of course, Mutex<T> have no indirections thus one couldn't expect Mutex<T> to pinnable (except when T is some kind of pointer).

You can always obtain a &_ to a pinned value, and a mutex lets you obtain &mut T given &Mutex<T>, so the inner value simply cannot be pinned.

One could imagine a PinnedMutex where locking it only gives you a Pin<&mut T>, and that would be fine. But the current API doesn't support it.

2 Likes

There's a lengthy discussion of this in the Rust standard library documentation for RefCell.

The problem here is that even hypothetical PinnedMutex can be moved, as whole in some other place in memory.

That's extremely fundamental property of Rust type system. You don't need any special bounds to use replace with any type.

And that means that to be able to provide some meaningful guarantees PinnedMutex would have to have some indirection, first, and, probably even more importantly, its “content” may still be “moved” somewhere with replace.

Yes, it would be a different content and from the mathematical proof of safety it may not matter… but people simply don't expect such capability to be preseint in the “pinned” type.

When you have explicit pointer-like type it's kinda-sorta not astonishing (reference is reference, it's not strange that it may be replaced) but with name PinnedMutex people wouldn't expect that at all.

i feel like anyone who needs a PinnedMutex wouldn't be surprised by the name. this name is already used

The question is not about name, it's about behavior. Note that PinnedMutex that you refer here can not be used by itself, to make anything pinned you still need to wrap it into Pin with some kind of pointer. Something like Pin<&PinnedMutex<…>>. This still includes explicit pointer outside of mutex – with appropriate expectations.

If you would try to hide the fact that pointer is involved — then you would have a problem.

No that's not true. You just require that the entire mutex is pinned too. The lock() method can take self: Pin<&Self> instead of &self to enforce this.

2 Likes

yes, to make something pinned you need to pin it, i don't see what the issue is.

note the original question :

here replacing Mutex by PinnedMutex makes pinning work.

let mut x: Pin<&PinnedMutex<T>> = todo!();
let mut g = x.lock().unwrap();
g.as_mut().set(T::new());

The question is simple :
"Can the T in a Mutex<T> be Pinned"
And the answer also is:
"no it cannot, but if you use a PinnedMutex instead then yes, but this one only works when pinned."

The issue is that it's not natural to do that.

Note that is not the original question. Original question was talking about Pin<Mutex<T>>.

This may or may not work, it's hard to say. Given the fact that so many try to use Box<Pin<T>> (which doesn't do anything useful) chances are high that this problem with happen to PinnedMutex that's supposed to be used with Pin.

But it's still better then PinnedMutex that would include Pin and Box inside and thus woud have very surprising semantic.

I'm not sure what's to talk about Pin<Mutex<T>>. Pretty much any useful Pin operation requires its generic argument to implement Deref, and Mutex<T> doesn't (and really can't unless you are ok with leaking the lock once and then always panicking/waiting forever)

What does people's confusion have to do with the soundness of a type's pinning implementation?

Ideally you want people to understand things without diffing through documentation. That's why we have long names like Box or Arc and don't stick to once considered acceptable two-letter names.

It's true that we may design sound system without thinking about how names that we use would be perceived at all… but would it be useful? We may end up with we have the source code, in a sense fiasco.

Names are bikesheddable, the design of types much less so. Both are required for success, but only the design is needed for something to work. It makes no sense to claim that PinnedMutex cannot be sound because it might be confusing to users.

We do have long names for some things, but Box and Arc are totally the wrong examples here, especially Arc which is not an actual arc but instead is short for Atomically Reference Counted.

Indeed one could imagine.[1]


  1. (context) ↩︎