#![deny(missing_docs)]
#[cfg(debug_assertions)]
extern crate backtrace;
#[cfg(debug_assertions)]
use std::cell::RefCell as StdRefCell;
use std::cell::{Cell, UnsafeCell};
use std::ops::{Deref, DerefMut};
pub struct RefCell<T: ?Sized> {
borrow: BorrowFlag,
value: UnsafeCell<T>,
}
#[cfg(not(debug_assertions))]
type Location = ();
#[cfg(debug_assertions)]
type Location = backtrace::Backtrace;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum BorrowState {
Reading,
Writing,
Unused,
}
struct BorrowFlag {
flag: Cell<usize>,
#[cfg(debug_assertions)]
locations: StdRefCell<Vec<Location>>,
}
const UNUSED: usize = 0;
const WRITING: usize = !0;
impl<T> RefCell<T> {
pub fn new(value: T) -> RefCell<T> {
RefCell {
borrow: BorrowFlag::new(),
value: UnsafeCell::new(value),
}
}
pub fn into_inner(self) -> T {
debug_assert!(self.borrow.flag.get() == UNUSED);
unsafe { self.value.into_inner() }
}
}
impl<T: ?Sized> RefCell<T> {
#[cfg_attr(debug_assertions, inline(never))]
pub fn borrow<'a>(&'a self) -> Ref<'a, T> {
match BorrowRef::new(&self.borrow) {
Some(b) => Ref {
_value: unsafe { &*self.value.get() },
_borrow: b,
},
None => self.panic("mutably borrowed"),
}
}
#[cfg_attr(debug_assertions, inline(never))]
pub fn borrow_mut<'a>(&'a self) -> RefMut<'a, T> {
match BorrowRefMut::new(&self.borrow) {
Some(b) => RefMut {
_value: unsafe { &mut *self.value.get() },
_borrow: b,
},
None => self.panic("borrowed"),
}
}
#[cfg(not(debug_assertions))]
fn panic(&self, msg: &str) -> ! {
panic!("RefCell<T> already {}", msg)
}
#[cfg(debug_assertions)]
#[allow(unused_must_use)]
fn panic(&self, msg: &str) -> ! {
let mut msg = format!("RefCell<T> already {}", msg);
let locations = self.borrow.locations.borrow();
if locations.len() > 0 {
msg.push_str("\ncurrent active borrows: \n");
for b in locations.iter() {
msg.push_str(&format!("-------------------------\n{:?}\n", b));
}
msg.push_str("\n\n");
}
panic!(msg)
}
}
#[cfg(not(debug_assertions))]
impl BorrowFlag {
#[inline]
fn new() -> BorrowFlag {
BorrowFlag { flag: Cell::new(UNUSED) }
}
#[inline]
fn push(&self, _caller: Location) {}
#[inline]
fn pop(&self) {}
}
#[cfg(debug_assertions)]
impl BorrowFlag {
fn new() -> BorrowFlag {
BorrowFlag {
flag: Cell::new(UNUSED),
locations: StdRefCell::new(Vec::new()),
}
}
fn push(&self, caller: Location) {
self.locations.borrow_mut().push(caller);
}
fn pop(&self) {
self.locations.borrow_mut().pop();
}
}
#[cfg(not(debug_assertions))]
#[inline]
fn get_caller() -> Location {}
#[inline(never)]
#[cfg(debug_assertions)]
fn get_caller() -> Location {
backtrace::Backtrace::new()
}
unsafe impl<T: ?Sized> Send for RefCell<T> where T: Send {}
impl<T: Clone> Clone for RefCell<T> {
#[inline]
fn clone(&self) -> RefCell<T> {
RefCell::new(self.borrow().clone())
}
}
impl<T:Default> Default for RefCell<T> {
#[inline]
fn default() -> RefCell<T> {
RefCell::new(Default::default())
}
}
impl<T: ?Sized + PartialEq> PartialEq for RefCell<T> {
#[inline]
fn eq(&self, other: &RefCell<T>) -> bool {
*self.borrow() == *other.borrow()
}
}
impl<T: ?Sized + Eq> Eq for RefCell<T> {}
struct BorrowRef<'b> {
borrow: &'b BorrowFlag,
}
impl<'b> BorrowRef<'b> {
#[cfg_attr(debug_assertions, inline(never))]
#[cfg_attr(not(debug_assertions), inline)]
fn new(borrow: &'b BorrowFlag) -> Option<BorrowRef<'b>> {
let flag = borrow.flag.get();
if flag == WRITING { return None }
borrow.flag.set(flag + 1);
borrow.push(get_caller());
Some(BorrowRef { borrow: borrow })
}
}
impl<'b> Drop for BorrowRef<'b> {
#[inline]
fn drop(&mut self) {
let flag = self.borrow.flag.get();
debug_assert!(flag != WRITING && flag != UNUSED);
self.borrow.flag.set(flag - 1);
self.borrow.pop();
}
}
pub struct Ref<'b, T: ?Sized + 'b> {
_value: &'b T,
_borrow: BorrowRef<'b>,
}
impl<'b, T: ?Sized> Deref for Ref<'b, T> {
type Target = T;
fn deref(&self) -> &T {
self._value
}
}
struct BorrowRefMut<'b> {
borrow: &'b BorrowFlag,
}
impl<'b> BorrowRefMut<'b> {
#[cfg_attr(debug_assertions, inline(never))]
#[cfg_attr(not(debug_assertions), inline)]
fn new(borrow: &'b BorrowFlag) -> Option<BorrowRefMut<'b>> {
if borrow.flag.get() != UNUSED { return None }
borrow.flag.set(WRITING);
borrow.push(get_caller());
Some(BorrowRefMut { borrow: borrow })
}
}
impl<'b> Drop for BorrowRefMut<'b> {
#[inline]
fn drop(&mut self) {
debug_assert!(self.borrow.flag.get() == WRITING);
self.borrow.flag.set(UNUSED);
self.borrow.pop();
}
}
pub struct RefMut<'b, T: ?Sized + 'b> {
_value: &'b mut T,
_borrow: BorrowRefMut<'b>,
}
impl<'b, T: ?Sized> Deref for RefMut<'b, T> {
type Target = T;
fn deref(&self) -> &T {
self._value
}
}
impl<'b, T: ?Sized> DerefMut for RefMut<'b, T> {
fn deref_mut(&mut self) -> &mut T {
self._value
}
}
#[cfg(test)]
mod tests {
use super::RefCell;
#[test]
fn ok_borrows() {
let a = RefCell::new(2);
let b = a.borrow();
let c = a.borrow();
assert_eq!(*b, 2);
assert_eq!(*c, 2);
drop((b, c));
let mut b = a.borrow_mut();
assert_eq!(*b, 2);
*b = 4;
drop(b);
assert_eq!(*a.borrow(), 4);
}
#[should_panic]
#[test]
fn bad_borrow_mut() {
let a = RefCell::new(2);
let _a = a.borrow();
a.borrow_mut();
}
#[should_panic]
#[test]
fn bad_borrow() {
let a = RefCell::new(2);
let _a = a.borrow_mut();
a.borrow();
}
}