In Rust there are two ways of accessing data:
-
either in the presence of potential aliasing (
&_
reference, e.g., theDeref
impl onArc
); -
or with guaranteed uniqueness (
&mut _
reference)- This is such a strong guarantee, that any kind of mutation is deemed acceptable, including moving data through
::core::mem::swap
/core::mem::replace
, etc.
- This is such a strong guarantee, that any kind of mutation is deemed acceptable, including moving data through
The Pin
is a wrapper used to restrict the power of the latter case: despite the uniqueness of a Pin<&mut _>
reference, mutation is still restricted, to avoid moves on position-sensitive data (!Unpin
), such as self-referential structs.
Hence, when having a Box<_>
, a unique-owner kind of pointer, &mut _
-ness is capable of transitively reaching the pointee (for<T> Box<T> : DerefMut<Target = T>
), and thus having Pin<Box<T>>
is indeed accurate to prevent movement mishaps.
With Arc<T>
(and Rc<T>
) however, since there is shared / aliased ownership, there is no impl<T> DerefMut for Arc<T>
, and we go back to using the "weak" &T
that does not permit moving the pointee.
- Regarding the
get_mut
andmake_mut
methods, the former will always fail as long as at least oneArc
remains leaked (i.e., after a call toArc::into_raw
and before the freeingArc::from_raw
), and in a similar fashion the latter will always clone the pointee. Hence the one leaked into FFI is never accessed by a&mut _
.
Thus, despite the good intention, what your initial code did seems best. And of course the callback should not mutate the given context
, except through Sync
-hronisation primitive such as Mutex<_>
or RwLock<_>
(in which case, by the way, Pin
would have been giving a false sense of satefy: since the pinning is not structural in a Mutex
/RwLock
, Pin<Arc<Mutex<T>>>
does never imply that T
is pinned).