// Copyright 2012 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! A mutable, nullable memory location use cast::transmute_mut; use prelude::*; use util::replace; /* A dynamic, mutable location. Similar to a mutable option type, but friendlier. */ #[mutable] #[deriving(Clone)] pub struct Cell { priv value: Option } impl DeepClone for Cell { fn deep_clone(&self) -> Cell { Cell{value: self.value.deep_clone()} } } impl cmp::Eq for Cell { fn eq(&self, other: &Cell) -> bool { (self.value) == (other.value) } fn ne(&self, other: &Cell) -> bool { !self.eq(other) } } /// Creates a new full cell with the given value. pub fn Cell(value: T) -> Cell { Cell { value: Some(value) } } pub fn empty_cell() -> Cell { Cell { value: None } } pub impl Cell { /// Yields the value, failing if the cell is empty. fn take(&self) -> T { let this = unsafe { transmute_mut(self) }; if this.is_empty() { fail!("attempt to take an empty cell"); } replace(&mut this.value, None).unwrap() } /// Returns the value, failing if the cell is full. fn put_back(&self, value: T) { let this = unsafe { transmute_mut(self) }; if !this.is_empty() { fail!("attempt to put a value back into a full cell"); } this.value = Some(value); } /// Returns true if the cell is empty and false if the cell is full. fn is_empty(&self) -> bool { self.value.is_none() } // Calls a closure with a reference to the value. fn with_ref(&self, op: &fn(v: &T) -> R) -> R { let v = self.take(); let r = op(&v); self.put_back(v); r } // Calls a closure with a mutable reference to the value. fn with_mut_ref(&self, op: &fn(v: &mut T) -> R) -> R { let mut v = self.take(); let r = op(&mut v); self.put_back(v); r } } #[test] fn test_basic() { let value_cell = Cell(~10); assert!(!value_cell.is_empty()); let value = value_cell.take(); assert!(value == ~10); assert!(value_cell.is_empty()); value_cell.put_back(value); assert!(!value_cell.is_empty()); } #[test] #[should_fail] #[ignore(cfg(windows))] fn test_take_empty() { let value_cell = empty_cell::<~int>(); value_cell.take(); } #[test] #[should_fail] #[ignore(cfg(windows))] fn test_put_back_non_empty() { let value_cell = Cell(~10); value_cell.put_back(~20); } #[test] fn test_with_ref() { let good = 6; let c = Cell(~[1, 2, 3, 4, 5, 6]); let l = do c.with_ref() |v| { v.len() }; assert_eq!(l, good); } #[test] fn test_with_mut_ref() { let good = ~[1, 2, 3]; let v = ~[1, 2]; let c = Cell(v); do c.with_mut_ref() |v| { v.push(3); } let v = c.take(); assert_eq!(v, good); }