diff options
| author | Steven Fackler <sfackler@gmail.com> | 2013-11-21 21:30:34 -0800 |
|---|---|---|
| committer | Steven Fackler <sfackler@gmail.com> | 2013-11-23 13:45:05 -0800 |
| commit | bdfaf04bd507bf99cff392c0a0c2df65ede96f69 (patch) | |
| tree | b946434aee84cc2a9d7bb8fbb0ff92bec5d3d6b6 /src/libstd/cell.rs | |
| parent | c6ca9abcc651423fe85f522cbd20f1e64463c36f (diff) | |
| download | rust-bdfaf04bd507bf99cff392c0a0c2df65ede96f69.tar.gz rust-bdfaf04bd507bf99cff392c0a0c2df65ede96f69.zip | |
Move mutable::Mut to cell::RefCell
Diffstat (limited to 'src/libstd/cell.rs')
| -rw-r--r-- | src/libstd/cell.rs | 302 |
1 files changed, 298 insertions, 4 deletions
diff --git a/src/libstd/cell.rs b/src/libstd/cell.rs index b33a18ed95d..019cd53be55 100644 --- a/src/libstd/cell.rs +++ b/src/libstd/cell.rs @@ -8,12 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Runtime move semantics +//! Types dealing with dynamic mutability #[missing_doc]; -use cast::transmute_mut; use prelude::*; +use cast; +use util::NonCopyable; + /* A dynamic, mutable location. @@ -36,7 +38,7 @@ impl<T> Cell<T> { /// Yields the value, failing if the cell is empty. pub fn take(&self) -> T { - let this = unsafe { transmute_mut(self) }; + let this = unsafe { cast::transmute_mut(self) }; if this.is_empty() { fail!("attempt to take an empty cell"); } @@ -46,7 +48,7 @@ impl<T> Cell<T> { /// Yields the value if the cell is full, or `None` if it is empty. pub fn take_opt(&self) -> Option<T> { - let this = unsafe { transmute_mut(self) }; + let this = unsafe { cast::transmute_mut(self) }; this.value.take() } @@ -72,3 +74,295 @@ fn test_take_empty() { value_cell.take(); value_cell.take(); } + + +/// A mutable memory location with dynamically checked borrow rules +#[no_freeze] +pub struct RefCell<T> { + priv value: T, + priv borrow: BorrowFlag, + priv nc: NonCopyable +} + +// Values [1, MAX-1] represent the number of `Ref` active +// (will not outgrow its range since `uint` is the size of the address space) +type BorrowFlag = uint; +static UNUSED: BorrowFlag = 0; +static WRITING: BorrowFlag = -1; + +impl<T> RefCell<T> { + /// Create a new `RefCell` containing `value` + pub fn new(value: T) -> RefCell<T> { + RefCell { + value: value, + borrow: UNUSED, + nc: NonCopyable + } + } + + /// Consumes the `RefCell`, returning the wrapped value. + pub fn unwrap(self) -> T { + assert!(self.borrow == UNUSED); + self.value + } + + unsafe fn as_mut<'a>(&'a self) -> &'a mut RefCell<T> { + cast::transmute_mut(self) + } + + /// Attempts to immutably borrow the wrapped value. + /// + /// The borrow lasts until the returned `Ref` exits scope. Multiple + /// immutable borrows can be taken out at the same time. + /// + /// Returns `None` if the value is currently mutably borrowed. + pub fn try_borrow<'a>(&'a self) -> Option<Ref<'a, T>> { + match self.borrow { + WRITING => None, + _ => { + unsafe { self.as_mut().borrow += 1; } + Some(Ref { parent: self }) + } + } + } + + /// Immutably borrows the wrapped value. + /// + /// The borrow lasts until the returned `Ref` exits scope. Multiple + /// immutable borrows can be taken out at the same time. + /// + /// # Failure + /// + /// Fails if the value is currently mutably borrowed. + pub fn borrow<'a>(&'a self) -> Ref<'a, T> { + match self.try_borrow() { + Some(ptr) => ptr, + None => fail!("RefCell<T> already mutably borrowed") + } + } + + /// Mutably borrows the wrapped value. + /// + /// The borrow lasts untile the returned `RefMut` exits scope. The value + /// cannot be borrowed while this borrow is active. + /// + /// Returns `None` if the value is currently borrowed. + pub fn try_borrow_mut<'a>(&'a self) -> Option<RefMut<'a, T>> { + match self.borrow { + UNUSED => unsafe { + let mut_self = self.as_mut(); + mut_self.borrow = WRITING; + Some(RefMut { parent: mut_self }) + }, + _ => None + } + } + + /// Mutably borrows the wrapped value. + /// + /// The borrow lasts untile the returned `RefMut` exits scope. The value + /// cannot be borrowed while this borrow is active. + /// + /// # Failure + /// + /// Fails if the value is currently borrowed. + pub fn borrow_mut<'a>(&'a self) -> RefMut<'a, T> { + match self.try_borrow_mut() { + Some(ptr) => ptr, + None => fail!("RefCell<T> already borrowed") + } + } + + /// Immutably borrows the wrapped value and applies `blk` to it. + /// + /// # Failure + /// + /// Fails if the value is currently mutably borrowed. + #[inline] + pub fn with<U>(&self, blk: |&T| -> U) -> U { + let ptr = self.borrow(); + blk(ptr.get()) + } + + /// Mutably borrows the wrapped value and applies `blk` to it. + /// + /// # Failure + /// + /// Fails if the value is currently borrowed. + #[inline] + pub fn with_mut<U>(&self, blk: |&mut T| -> U) -> U { + let mut ptr = self.borrow_mut(); + blk(ptr.get()) + } +} + +impl<T: Clone> Clone for RefCell<T> { + fn clone(&self) -> RefCell<T> { + let x = self.borrow(); + RefCell::new(x.get().clone()) + } +} + +impl<T: DeepClone> DeepClone for RefCell<T> { + fn deep_clone(&self) -> RefCell<T> { + let x = self.borrow(); + RefCell::new(x.get().deep_clone()) + } +} + +impl<T: Eq> Eq for RefCell<T> { + fn eq(&self, other: &RefCell<T>) -> bool { + let a = self.borrow(); + let b = other.borrow(); + a.get() == b.get() + } +} + +/// Wraps a borrowed reference to a value in a `RefCell` box. +pub struct Ref<'box, T> { + priv parent: &'box RefCell<T> +} + +#[unsafe_destructor] +impl<'box, T> Drop for Ref<'box, T> { + fn drop(&mut self) { + assert!(self.parent.borrow != WRITING && self.parent.borrow != UNUSED); + unsafe { self.parent.as_mut().borrow -= 1; } + } +} + +impl<'box, T> Ref<'box, T> { + /// Retrieve an immutable reference to the stored value. + #[inline] + pub fn get<'a>(&'a self) -> &'a T { + &self.parent.value + } +} + +/// Wraps a mutable borrowed reference to a value in a `RefCell` box. +pub struct RefMut<'box, T> { + priv parent: &'box mut RefCell<T> +} + +#[unsafe_destructor] +impl<'box, T> Drop for RefMut<'box, T> { + fn drop(&mut self) { + assert!(self.parent.borrow == WRITING); + self.parent.borrow = UNUSED; + } +} + +impl<'box, T> RefMut<'box, T> { + /// Retrieve a mutable reference to the stored value. + #[inline] + pub fn get<'a>(&'a mut self) -> &'a mut T { + &mut self.parent.value + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn double_imm_borrow() { + let x = RefCell::new(0); + let _b1 = x.borrow(); + x.borrow(); + } + + #[test] + fn no_mut_then_imm_borrow() { + let x = RefCell::new(0); + let _b1 = x.borrow_mut(); + assert!(x.try_borrow().is_none()); + } + + #[test] + fn no_imm_then_borrow_mut() { + let x = RefCell::new(0); + let _b1 = x.borrow(); + assert!(x.try_borrow_mut().is_none()); + } + + #[test] + fn no_double_borrow_mut() { + let x = RefCell::new(0); + let _b1 = x.borrow_mut(); + assert!(x.try_borrow_mut().is_none()); + } + + #[test] + fn imm_release_borrow_mut() { + let x = RefCell::new(0); + { + let _b1 = x.borrow(); + } + x.borrow_mut(); + } + + #[test] + fn mut_release_borrow_mut() { + let x = RefCell::new(0); + { + let _b1 = x.borrow_mut(); + } + x.borrow(); + } + + #[test] + fn double_borrow_single_release_no_borrow_mut() { + let x = RefCell::new(0); + let _b1 = x.borrow(); + { + let _b2 = x.borrow(); + } + assert!(x.try_borrow_mut().is_none()); + } + + #[test] + fn with_ok() { + let x = RefCell::new(0); + assert_eq!(1, x.with(|x| *x+1)); + } + + #[test] + #[should_fail] + fn mut_borrow_with() { + let x = RefCell::new(0); + let _b1 = x.borrow_mut(); + x.with(|x| *x+1); + } + + #[test] + fn borrow_with() { + let x = RefCell::new(0); + let _b1 = x.borrow(); + assert_eq!(1, x.with(|x| *x+1)); + } + + #[test] + fn with_mut_ok() { + let x = RefCell::new(0); + x.with_mut(|x| *x += 1); + let b = x.borrow(); + assert_eq!(1, *b.get()); + } + + #[test] + #[should_fail] + fn borrow_with_mut() { + let x = RefCell::new(0); + let _b = x.borrow(); + x.with_mut(|x| *x += 1); + } + + #[test] + #[should_fail] + fn discard_doesnt_unborrow() { + let x = RefCell::new(0); + let _b = x.borrow(); + let _ = _b; + let _b = x.borrow_mut(); + } +} |
