Motivation
&T and &mut T literally mean immutable references and mutable references, but more exactly, shared references and exclusive references. &mut T is mutable, also exclusive (assuming that we are not talking about UnsafePinned). That's not bad. However, &T is more complicated because of interior mutability. &T is immutable only when T: Freeze. For T: !Freeze, &T is not only mutable but also shared. What's more, there are no immutable references for T: !Freeze except wrapping T into a new type.
Definition
In this doc, the shared reference &T in current Rust is split up to 3 kinds of references:
- the immutable reference
&const TwhereT: Freeze, i.e. the referee of an&'a const Tcan never be mutated during the lifetime'a. - the shared mutable reference
&shr mut T, which is (semantically) really similar to&UnsafeCell<T>in current Rust. The referee of an&'a shr mut Tcan be mutated but cannot be moved or dropped during the lifetime'a. Besides that,&shr mut Tcan also be freely copied[1]. - the shared immutable reference
&shr T[2], the immutable counterpart of&shr mut T. The difference between&const Tand&shr Tis: the referee of&const Tis guaranteed to be immutable during its lifetime, but that of&shr Tis not. The only guarantee of&shr Tis that the referee cannot be mutated by this reference itself, but there may be&shr mut Treferring to the same value, and their lifetimes can overlap.
The (exclusively) mutable reference &mut T remains unchanged in this doc.
Here is a table of the total 4 kinds of references. The main idea is: &shr T and &shr mut T cannot coexist with &const T or &mut T. In LLVM's terms, &const T and &mut T are noalias, but &shr T and &shr mut T are not.
| Reference Kind | Current Rust (Approximate) | This Doc |
|---|---|---|
| immutable, no alias | &T, except T contains UnsafeCell |
&const T |
| immutable, may alias | &T, and T contains UnsafeCell |
&shr T |
| mutable, no alias | &mut T, except T is UnsafePinned |
&mut T |
| mutable, may alias | &mut UnsafePinned<T> |
&shr mut T |
As a result, UnsafeCell and UnsafePinned is no longer needed. UnsafeCell::get and UnsafePinned::get can be replaced by raw (re)borrows of &shr T and &shr mut T.
Here is the coercion/reborrow rules among the 4 kinds of references.
| From \ To | &const T |
&shr T |
&mut T |
&shr mut T |
|---|---|---|---|---|
&const T |
√ | √ | × | × |
&shr T |
× | √ | × | × |
&mut T |
√ (Reborrow) | √ (Reborrow) | √ (Reborrow) | √ (Reborrow) |
&shr mut T |
× | √ | × | √ |
The thread safety rules has be slightly changed:
Send: the same as current Rust.SyncV2: indicate the&shr Tand&shr mut Tcan be sent across threads.
Note that SyncV2 is kind of different with Sync in current Rust. Many primitive types (integers, bool, str, etc.) become !SyncV2 because &shr mut T (quite similar to &Cell<T> in current Rust) can not be shared across threads.
| Type | Send where |
SyncV2 where |
|---|---|---|
&const T |
T: Send |
T: Send |
&shr T |
T: SyncV2 |
T: SyncV2 |
&mut T |
T: Send |
T: Send |
&shr mut T |
T: SyncV2[3] |
T: SyncV2 |
Rc<T> |
never | never |
Arc<T> |
T: Send + SyncV2 |
T: SyncV2 |
Mutex<T> |
T: Send |
always |
RwLock<T> |
T: Send |
T: SyncV2 |
ReentrantLock<T> |
T: Send |
T: SyncV2 |
Exclusive<T>[4] |
T: Send |
always |
Libaray APIs
DerefConst, DerefMut, DerefShr, and DerefShrMut
pub trait DerefTarget {
type Target;
}
pub trait DerefConst: DerefTarget {
fn deref_const(&self) -> &const Self::Target;
}
pub trait DerefMutV2: DerefTarget {
fn deref_mut_v2(&mut self) -> &mut Self::Target;
}
pub trait DerefShr : DerefTarget {
fn deref_shr(&shr self) -> &shr Self::Target;
}
pub trait DerefShrMut : DerefTarget {
fn deref_shr_mut(&shr mut self) -> &shr mut Self::Target;
}
Rc<T>, Arc<T>, CloneV2 and CopyMut
Deep copy and shallow copy of Rc<T> and Arc<T> become completely different, as deep copy needs an immutable reference, but shallow copy needs a shared mutable reference.
pub trait Copy {}
pub trait CloneV2 {
// Deep copy always.
fn clone_v2(&const self) -> Self;
}
impl<T: Copy> CloneV2 for T {}
/// # Safety
/// The implementation must guarantee that after `on_copy` is called,
/// the newly copied value cannot cause a double-free error together with
/// the original value.
pub unsafe trait CopyMut {
// Shallow copy only. To copy a `CopyMut` value, first call `on_copy`
// on the original value to allow a new duplicated value to be created,
// then call `memcpy` to create a new duplicated value.
fn on_copy(this: &shr mut Self);
}
impl<T: Copy> CopyMut for T {}
impl<T> CopyMut for Rc<T> {
fn on_copy(this: &shr mut Self) {
unsafe { Rc::increment_strong_count(Rc::as_ptr(this)) }
}
}
impl<T> DerefTarget for Rc<T> {
type Target = T;
}
impl<T> DerefConst for Rc<T> { /* ... */ }
impl<T> DerefShr for Rc<T> { /* ... */ }
impl<T> DerefShrMut for Rc<T> { /* ... */ }
// No DerefMutV2 for Rc<T>
impl<T> !Send for Rc<T> {}
impl<T> !SyncV2 for Rc<T> {}
impl<T> CopyMut for Arc<T> { /* ... */ }
impl<T> DerefTarget for Arc<T> {
type Target = T;
}
impl<T> DerefConst for Arc<T> { /* ... */ }
impl<T: SyncV2> DerefShr for Arc<T> { /* ... */ }
impl<T: SyncV2> DerefShrMut for Arc<T> { /* ... */ }
// No DerefMutV2 for Arc<T>
// Here `T: SyncV2` is unnecessary. Note that
// `DerefShr` and `DerefShrMut` requires `SyncV2`,
// so for `T: !SyncV2`, `Arc<T>` is intended for sharing
// read-only data.
unsafe impl<T: Send> Send for Arc<T> { /* ... */ }
unsafe impl<T: SyncV2> SyncV2 for Arc<T> { /* ... */ }
Vec<T>, and LinkedList<T>
impl<T> Vec<T> {
// `push` needs an exclusive mutable reference, because it may realloc.
pub fn push(&mut self, value: T) { /* ... */ }
// `push_within_capacity` only needs a shared mutable reference,
// as it never realloc.
pub fn push_within_capacity(
&shr mut self,
value: T,
) -> Result<(), T> { /* ... */ }
/// `pop` may drop the value inside the `Vec`, so `&shr mut self` is not
/// enough. It must take an `&mut self`.
pub fn pop(&mut self) -> Option<T> { /* ... */ }
}
impl<T> LinkedList<T> {
// Adding new elements to a linked list will not reallocate,
// so an `&shr mut self` is enough for memory safety.
pub fn push_back(&shr mut self, value: T) { /* ... */ }
pub fn push_front(&shr mut self, value: T) { /* ... */ }
// However, removing elements from a linked list can drop,
// so an `&mut self` must be required.
pub fn pop_back(&mut self) -> Option<T> { /* ... */ }
pub fn pop_front(&mut self) -> Option<T> { /* ... */ }
}
Mutex<T>, ReentrantLock<T>, and RwLock<T> (non-poison versions)
For Mutex<T> and ReentrantLock<T>, their API are almost the same. There is at most one thread can access the data, so T: SyncV2 is not needed for DerefShr and DerefShrMut.
But for RwLock<T>, there can be more than one threads holding shared references (&const T, &shr T, and &shr mut T), so DerefShr and DerefShrMut requires T: SyncV2.
impl<T> Mutex<T> {
pub fn lock(&shr mut self) -> MutexGuard<'_, T> { /* ... */ }
// get `&const T` without locking
pub fn get_ref(&self) -> &const T { /* ... */ }
// get `&mut T` without locking
pub fn get_mut(&mut self) -> &mut T { /* ... */ }
}
unsafe impl<T: Send> Send for Mutex<T> {}
unsafe impl<T: Send> SyncV2 for Mutex<T> {}
impl<T> DerefShr for MutexGuard<'_, T> { /* ... */ }
impl<T> DerefShrMut for MutexGuard<'_, T> { /* ... */ }
impl<T> DerefMutV2 for MutexGuard<'_, T> { /* ... */ }
impl<T> ReentrantLock<T> {
pub fn lock(&shr mut self) -> ReentrantLockGuard<'_, T> { /* ... */ }
// get `&const T` without locking
pub fn get_ref(&const self) -> &const T { /* ... */ }
// get `&mut T` without locking
pub fn get_mut(&mut self) -> &mut T { /* ... */ }
}
unsafe impl<T: Send> Send for ReentrantLock<T> { /* ... */ }
unsafe impl<T: Send> SyncV2 for ReentrantLock<T> { /* ... */ }
impl<T> DerefShr for ReentrantLockGuard<'_, T> { /* ... */ }
impl<T> DerefShrMut for ReentrantLockGuard<'_, T> { /* ... */ }
impl<T> RwLock<T> {
pub fn read(&shr mut self) -> RwLockReadGuard<'_, T> { /* ... */ }
pub fn write(&shr mut self) -> RwLockWriteGuard<'_, T> { /* ... */ }
// get `&const T` without locking
pub fn get_ref(&const self) -> &const T { /* ... */ }
// get `&mut T` without locking
pub fn get_mut(&mut self) -> &mut T { /* ... */ }
}
unsafe impl<T: Send> Send for RwLock<T> { /* ... */ }
unsafe impl<T: Send> SyncV2 for RwLock<T> { /* ... */ }
impl<T: SyncV2> DerefShr for RwLockReadGuard<'_, T> { /* ... */ }
impl<T: SyncV2> DerefShrMut for RwLockReadGuard<'_, T> { /* ... */ }
impl<T> DerefShr for RwLockWriteGuard<'_, T> { /* ... */ }
impl<T> DerefShrMut for RwLockWriteGuard<'_, T> { /* ... */ }
impl<T> DerefMutV2 for RwLockWriteGuard<'_, T> { /* ... */ }
AtomicI*, AtomicU*, AtomicBool and AtomicPtr
impl Atomic{{T}} {
// using `&shr self` or `&shr mut self` can distinguish whether the
// method will mutate the data
pub fn load(&shr self, ordering: Ordering) -> {{T}} { /* ... */ }
pub fn store(&shr mut self, value: {{T}}, ordering: Ordering) { /* ... */ }
// ...
}