about summary refs log tree commit diff
path: root/src/libstd/cell.rs
diff options
context:
space:
mode:
authorSteven Fackler <sfackler@gmail.com>2013-11-21 21:30:34 -0800
committerSteven Fackler <sfackler@gmail.com>2013-11-23 13:45:05 -0800
commitbdfaf04bd507bf99cff392c0a0c2df65ede96f69 (patch)
treeb946434aee84cc2a9d7bb8fbb0ff92bec5d3d6b6 /src/libstd/cell.rs
parentc6ca9abcc651423fe85f522cbd20f1e64463c36f (diff)
downloadrust-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.rs302
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();
+    }
+}