#![unstable = "almost stable, but not the macro parts"]
#![no_std]
#![feature(unsafe_destructor, macro_rules, phase, default_type_params)]
#![warn(bad_style, unused, missing_docs)]
#[phase(plugin, link)] extern crate core;
extern crate serialize;
extern crate rand;
extern crate collections;
#[cfg(test)] extern crate std;
use core::cell::{Cell, UnsafeCell};
use core::default::Default;
use core::fmt;
use core::kinds::marker;
use serialize::{Encoder, Decoder, Encodable, Decodable};
use rand::{Rand, Rng};
use core::hash::{Hash, sip};
use core::prelude::{Option, Clone, Result, PartialEq, Eq, PartialOrd, Ord, Ordering, Deref, Drop};
const MUTATING: uint = -1;
#[stable]
pub struct MuCell<T> {
value: UnsafeCell<T>,
borrows: Cell<uint>,
nocopy: marker::NoCopy,
noshare: marker::NoSync,
}
#[stable]
impl<T> MuCell<T> {
#[inline]
#[stable]
pub fn new(value: T) -> MuCell<T> {
MuCell {
value: UnsafeCell::new(value),
borrows: Cell::new(0),
nocopy: marker::NoCopy,
noshare: marker::NoSync,
}
}
#[inline]
#[stable]
pub fn borrow_mut(&mut self) -> &mut T {
unsafe { &mut *self.value.get() }
}
#[inline]
#[stable]
pub fn borrow(&self) -> Ref<T> {
let borrows = self.borrows.get();
debug_assert!(borrows != MUTATING);
self.borrows.set(borrows + 1);
Ref { _parent: self }
}
#[inline]
#[stable]
pub fn try_mutate(&self, mutator: |&mut T|) -> bool {
if self.borrows.get() == 0 {
self.borrows.set(MUTATING);
mutator(unsafe { &mut *self.value.get() });
self.borrows.set(0);
true
} else {
false
}
}
}
#[stable]
pub struct Ref<'a, T: 'a> {
_parent: &'a MuCell<T>,
}
#[unsafe_destructor]
#[unstable = "trait is not stable"]
impl<'a, T: 'a> Drop for Ref<'a, T> {
fn drop(&mut self) {
self._parent.borrows.set(self._parent.borrows.get() - 1);
}
}
#[unstable = "trait is not stable"]
impl<'a, T: 'a> Deref<T> for Ref<'a, T> {
fn deref(&self) -> &T {
unsafe { &*self._parent.value.get() }
}
}
#[unstable = "trait is not stable"]
impl<T: PartialEq> PartialEq for MuCell<T> {
fn eq(&self, other: &MuCell<T>) -> bool {
*self.borrow() == *other.borrow()
}
}
#[unstable = "trait is not stable"]
impl<T: Eq> Eq for MuCell<T> { }
#[unstable = "trait is not stable"]
impl<T: PartialOrd> PartialOrd for MuCell<T> {
fn partial_cmp(&self, other: &MuCell<T>) -> Option<Ordering> {
self.borrow().partial_cmp(&*other.borrow())
}
}
#[unstable = "trait is not stable"]
impl<T: Ord> Ord for MuCell<T> {
fn cmp(&self, other: &MuCell<T>) -> Ordering {
self.borrow().cmp(&*other.borrow())
}
}
#[unstable = "trait is not stable"]
impl<T: Default> Default for MuCell<T> {
fn default() -> MuCell<T> {
MuCell::new(Default::default())
}
}
#[unstable = "trait is not stable"]
impl<T: Clone> Clone for MuCell<T> {
fn clone(&self) -> MuCell<T> {
MuCell::new(self.borrow().clone())
}
}
macro_rules! impl_fmt {
($($trait_name:ident)*) => {$(
#[unstable = "trait is not stable"]
impl<T: fmt::$trait_name> fmt::$trait_name for MuCell<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.borrow().fmt(f)
}
}
)*}
}
impl_fmt!(Show Octal Binary LowerHex UpperHex Pointer LowerExp UpperExp);
#[unstable = "trait is not stable"]
impl<S: Encoder<E>, E, T: Encodable<S, E>> Encodable<S, E> for MuCell<T> {
fn encode(&self, s: &mut S) -> Result<(), E> {
self.borrow().encode(s)
}
}
#[unstable = "trait is not stable"]
impl<D: Decoder<E>, E, T: Decodable<D, E>> Decodable<D, E> for MuCell<T> {
fn decode(d: &mut D) -> Result<MuCell<T>, E> {
Decodable::decode(d).map(|x| MuCell::new(x))
}
}
#[unstable = "trait is not stable"]
impl<T: Rand> Rand for MuCell<T> {
fn rand<R: Rng>(rng: &mut R) -> MuCell<T> {
MuCell::new(Rand::rand(rng))
}
}
#[unstable = "trait is not stable"]
impl<T: Hash<S>, S = sip::SipState> Hash<S> for MuCell<T> {
fn hash(&self, state: &mut S) {
self.borrow().hash(state)
}
}
#[macro_export]
#[experimental]
macro_rules! mucell_ref_type {
(
$(#[$attr:meta])* // suggestions: doc, stability markers
struct $ref_name:ident<'a>($ty:ty)
impl Deref<$deref:ty>
data: $data_ty:ty = |$data_ident:ident| $data_expr:expr
) => {
$(#[$attr])*
pub struct $ref_name<'a> {
_parent: Ref<'a, $ty>,
_data: $data_ty,
}
#[unstable = "still a little experimental, like the whole macro"]
impl<'a> $ref_name<'a> {
#[unstable = "still a little experimental, like the whole macro"]
fn from(cell: &'a MuCell<$ty>) -> $ref_name<'a> {
let parent = cell.borrow();
let $data_ident: &'a $ty = unsafe { &*(&*parent as *const $ty) };
let data = $data_expr;
$ref_name {
_parent: parent,
_data: data,
}
}
}
#[unstable = "trait is not stable"]
impl<'a> Deref<$deref> for $ref_name<'a> {
fn deref<'b>(&'b self) -> &'b $deref {
&*self._data
}
}
}
}
#[test]
#[should_fail]
fn test_borrow_in_try_mutate() {
let a = MuCell::new(());
a.try_mutate(|_| { let _ = a.borrow(); });
}
#[test]
fn test_try_mutate_in_try_mutate() {
let a = MuCell::new(());
assert!(a.try_mutate(|_| assert!(!a.try_mutate(|_| unreachable!()))));
}