use std::cell::{Cell, UnsafeCell};
use std::ops::{Deref, DerefMut, Drop};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum State {
Unborrowed,
Updating,
Fixed,
}
#[derive(Debug)]
pub struct MutOnce<T> {
value: UnsafeCell<T>,
state: Cell<State>,
}
impl<T> MutOnce<T> {
#[inline]
pub const fn new(value: T) -> Self {
Self {
value: UnsafeCell::new(value),
state: Cell::new(State::Unborrowed),
}
}
#[inline]
pub fn get_mut(&self) -> RefMut<'_, T> {
match self.state.get() {
State::Unborrowed => {
self.state.replace(State::Updating);
RefMut { target: self }
},
State::Updating => panic!("already mutably borrowed"),
State::Fixed => panic!("no longer mutable"),
}
}
#[inline]
pub fn get_ref(&self) -> &T {
match self.state.get() {
State::Unborrowed => { self.state.replace(State::Fixed); },
State::Updating => panic!("still mutably borrowed"),
State::Fixed => {},
}
unsafe { &*self.value.get() }
}
#[inline]
pub fn is_fixed(&self) -> bool {
self.state.get() == State::Fixed
}
#[inline]
pub fn into_inner(self) -> T {
self.value.into_inner()
}
}
impl<T: Default> Default for MutOnce<T> {
#[inline]
fn default() -> MutOnce<T> {
MutOnce::new(T::default())
}
}
impl<T> From<T> for MutOnce<T> {
#[inline]
fn from(t: T) -> MutOnce<T> {
MutOnce::new(t)
}
}
#[derive(Debug)]
pub struct RefMut<'a, T> {
target: &'a MutOnce<T>,
}
impl<'a, T> Deref for RefMut<'a, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { &*self.target.value.get() }
}
}
impl<'a, T> DerefMut for RefMut<'a, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.target.value.get() }
}
}
impl<'a, T> Drop for RefMut<'a, T> {
#[inline]
fn drop(&mut self) {
debug_assert_eq!(self.target.state.get(), State::Updating);
self.target.state.replace(State::Unborrowed);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn repeated_muts() {
let mo = MutOnce::new(Vec::new());
{
let mut mutvec = mo.get_mut();
mutvec.push(1);
mutvec.push(2);
}
{
let mut mutvec = mo.get_mut();
mutvec.push(3);
}
let vec = mo.get_ref();
assert_eq!(vec[0], 1);
assert_eq!(vec[1], 2);
assert_eq!(vec[2], 3);
}
#[test]
fn multiple_refs() {
let mo = MutOnce::new(Vec::new());
{
let mut mutvec = mo.get_mut();
mutvec.push(1);
mutvec.push(2);
mutvec.push(3);
}
let vec1 = mo.get_ref();
let vec2 = mo.get_ref();
assert_eq!(vec1[0], 1);
assert_eq!(vec2[1], 2);
assert_eq!(vec1[2], 3);
}
#[test]
fn temporary_value() {
let mo = MutOnce::new(Vec::new());
mo.get_mut().push(1);
mo.get_mut().push(2);
assert_eq!(mo.get_ref()[0], 1);
assert_eq!(mo.get_ref()[1], 2);
}
#[test]
#[should_panic(expected = "still mutably borrowed")]
fn ref_while_mut() {
let mo = MutOnce::new(Vec::new());
let mut mutvec = mo.get_mut();
mutvec.push(1);
assert_eq!(mo.get_ref()[0], 1);
}
#[test]
#[should_panic(expected = "no longer mutable")]
fn mut_after_ref() {
let mo = MutOnce::new(Vec::new());
assert_eq!(mo.get_ref().len(), 0);
mo.get_mut().push(1);
}
#[test]
#[should_panic(expected = "already mutably borrowed")]
fn multiple_muts() {
let mo = MutOnce::new(Vec::new());
let mut mutvec1 = mo.get_mut();
let mut mutvec2 = mo.get_mut();
mutvec1.push(1);
mutvec2.push(2);
}
#[test]
fn into_inner() {
let mo = MutOnce::new(Vec::new());
mo.get_mut().push(1);
mo.get_mut().push(7);
assert_eq!(mo.into_inner(), vec![1, 7])
}
#[test]
fn default() {
let mo = MutOnce::<u32>::default();
*mo.get_mut() += 9;
assert_eq!(*mo.get_ref(), 9);
}
#[test]
fn from() {
let mo: MutOnce<_> = From::from(0);
*mo.get_mut() += 9;
assert_eq!(*mo.get_ref(), 9);
let mo: MutOnce<_> = 0.into();
*mo.get_mut() += 9;
assert_eq!(*mo.get_ref(), 9);
}
}