Provide cheap access via &mut for types with inner mutability


#1

Currently, if you have a Cell, RefCell, RWLock or Mutex, you can only access it via the various methods with either (or both) dynamic validity checks and restrictions on the operations that can occur. All of these are designed to be used in shared locations and all methods for interacting with them take &self

However, if you can get a &mut self, then essentially none of these restrictions are required, since it’s known that nothing else can be interacting with the value and there’s no other borrows. That is we could provide &mut self methods that yield references into Cells (and maybe atomic types) and direct access to the contents of Mutex (etc.) without any synchronisation or dynamic borrow checks, in code:

impl<T> Cell<T> {
    fn access_uniquely<'a>(&'a mut self) -> &'a mut T {
        unsafe { &mut *self.value.get() }
    }
}
impl<T> Mutex<T> {
    fn access_uniquely<'a>(&'a mut self) -> &'a mut T {
        unsafe { &mut *self.data.get() }
    }
}

There’s at least two use-cases I can think of:

  • more flexible in-place initialisation of types: if you create a Mutex<ComplexDataType> you may wish to update parts of it progressively (or conditionally), but before it gets shared among threads, i.e. when &mut access is still possible

  • reading data out of a context type for a fork-join operation, i.e. currently one would write something like

      let x = Mutex::new(0);
      let new_vector = {
          par_map(some_slice, ref |element| {
              // complicated stuff that occasionally touches the reference to `x`
          })
      };
      println!("{}", *x.lock());
    

    but the last line will always succeed, as there’s no sharing at all. The change suggested in this post would make it possible for that operation to be free.

Does this sound crazy and/or desirable?


#2

+1 I think this sounds like a great idea. Are there any downsides to having an API like this or is it merely a question of API bloat?


#3

You can also implement Mutex::<T>::unwrap(self) -> T for the same type of use case.