// Copyright 2012-2013 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. //! Types dealing with dynamic mutability use cast; use clone::Clone; use cmp::Eq; use fmt; use kinds::{marker, Copy}; use ops::{Deref, DerefMut, Drop}; use option::{None, Option, Some}; use ty::Unsafe; /// A mutable memory location that admits only `Copy` data. pub struct Cell { value: Unsafe, noshare: marker::NoShare, } impl Cell { /// Creates a new `Cell` containing the given value. pub fn new(value: T) -> Cell { Cell { value: Unsafe::new(value), noshare: marker::NoShare, } } /// Returns a copy of the contained value. #[inline] pub fn get(&self) -> T { unsafe{ *self.value.get() } } /// Sets the contained value. #[inline] pub fn set(&self, value: T) { unsafe { *self.value.get() = value; } } } impl Clone for Cell { fn clone(&self) -> Cell { Cell::new(self.get()) } } impl Eq for Cell { fn eq(&self, other: &Cell) -> bool { self.get() == other.get() } } impl fmt::Show for Cell { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f.buf, r"Cell \{ value: {} \}", self.get()) } } /// A mutable memory location with dynamically checked borrow rules pub struct RefCell { value: Unsafe, borrow: BorrowFlag, nocopy: marker::NoCopy, noshare: marker::NoShare, } // 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 RefCell { /// Create a new `RefCell` containing `value` pub fn new(value: T) -> RefCell { RefCell { value: Unsafe::new(value), nocopy: marker::NoCopy, noshare: marker::NoShare, borrow: UNUSED, } } /// Consumes the `RefCell`, returning the wrapped value. pub fn unwrap(self) -> T { assert!(self.borrow == UNUSED); unsafe{self.value.unwrap()} } unsafe fn as_mut<'a>(&'a self) -> &'a mut RefCell { 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> { 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 already mutably borrowed") } } /// Mutably borrows the wrapped value. /// /// The borrow lasts until 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> { 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 until 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 already borrowed") } } } impl Clone for RefCell { fn clone(&self) -> RefCell { RefCell::new(self.borrow().clone()) } } impl Eq for RefCell { fn eq(&self, other: &RefCell) -> bool { *self.borrow() == *other.borrow() } } /// Wraps a borrowed reference to a value in a `RefCell` box. pub struct Ref<'b, T> { parent: &'b RefCell } #[unsafe_destructor] impl<'b, T> Drop for Ref<'b, T> { fn drop(&mut self) { assert!(self.parent.borrow != WRITING && self.parent.borrow != UNUSED); unsafe { self.parent.as_mut().borrow -= 1; } } } impl<'b, T> Deref for Ref<'b, T> { #[inline] fn deref<'a>(&'a self) -> &'a T { unsafe { &*self.parent.value.get() } } } /// Wraps a mutable borrowed reference to a value in a `RefCell` box. pub struct RefMut<'b, T> { parent: &'b mut RefCell } #[unsafe_destructor] impl<'b, T> Drop for RefMut<'b, T> { fn drop(&mut self) { assert!(self.parent.borrow == WRITING); self.parent.borrow = UNUSED; } } impl<'b, T> Deref for RefMut<'b, T> { #[inline] fn deref<'a>(&'a self) -> &'a T { unsafe { &*self.parent.value.get() } } } impl<'b, T> DerefMut for RefMut<'b, T> { #[inline] fn deref_mut<'a>(&'a mut self) -> &'a mut T { unsafe { &mut *self.parent.value.get() } } } #[cfg(test)] mod test { use super::*; #[test] fn smoketest_cell() { let x = Cell::new(10); assert_eq!(x, Cell::new(10)); assert_eq!(x.get(), 10); x.set(20); assert_eq!(x, Cell::new(20)); assert_eq!(x.get(), 20); let y = Cell::new((30, 40)); assert_eq!(y, Cell::new((30, 40))); assert_eq!(y.get(), (30, 40)); } #[test] fn cell_has_sensible_show() { use str::StrSlice; let x = Cell::new("foo bar"); assert!(format!("{}", x).contains(x.get())); x.set("baz qux"); assert!(format!("{}", x).contains(x.get())); } #[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] #[should_fail] fn discard_doesnt_unborrow() { let x = RefCell::new(0); let _b = x.borrow(); let _ = _b; let _b = x.borrow_mut(); } }