Skip to content

Commit

Permalink
Move mutable::Mut to cell::RefCell
Browse files Browse the repository at this point in the history
  • Loading branch information
sfackler committed Nov 23, 2013
1 parent c6ca9ab commit bdfaf04
Show file tree
Hide file tree
Showing 11 changed files with 335 additions and 350 deletions.
302 changes: 298 additions & 4 deletions src/libstd/cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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");
}
Expand All @@ -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()
}

Expand All @@ -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();
}
}
14 changes: 7 additions & 7 deletions src/libstd/gc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,26 +55,26 @@ impl<T: DeepClone + Send + 'static> DeepClone for Gc<T> {
#[cfg(test)]
mod tests {
use super::*;
use cell::Cell;
use cell::RefCell;

#[test]
fn test_clone() {
let x = Gc::new(Cell::new(5));
let x = Gc::new(RefCell::new(5));
let y = x.clone();
do x.borrow().with_mut_ref |inner| {
do x.borrow().with_mut |inner| {
*inner = 20;
}
assert_eq!(y.borrow().take(), 20);
assert_eq!(y.borrow().with(|x| *x), 20);
}

#[test]
fn test_deep_clone() {
let x = Gc::new(Cell::new(5));
let x = Gc::new(RefCell::new(5));
let y = x.deep_clone();
do x.borrow().with_mut_ref |inner| {
do x.borrow().with_mut |inner| {
*inner = 20;
}
assert_eq!(y.borrow().take(), 5);
assert_eq!(y.borrow().with(|x| *x), 5);
}

#[test]
Expand Down
1 change: 0 additions & 1 deletion src/libstd/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@ pub mod result;
pub mod either;
pub mod hashmap;
pub mod cell;
pub mod mutable;
pub mod trie;


Expand Down
Loading

5 comments on commit bdfaf04

@bors
Copy link
Contributor

@bors bors commented on bdfaf04 Nov 24, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saw approval from cmr
at sfackler@bdfaf04

@bors
Copy link
Contributor

@bors bors commented on bdfaf04 Nov 24, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merging sfackler/rust/mut = bdfaf04 into auto

@bors
Copy link
Contributor

@bors bors commented on bdfaf04 Nov 24, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sfackler/rust/mut = bdfaf04 merged ok, testing candidate = 33375a3

@bors
Copy link
Contributor

@bors bors commented on bdfaf04 Nov 24, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bors
Copy link
Contributor

@bors bors commented on bdfaf04 Nov 24, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast-forwarding master to auto = 33375a3

Please sign in to comment.