#![deny(missing_docs)]
pub mod error {
#[cfg(debug_assertions)]
fn locations_display(locations: &[super::Location]) -> String {
locations
.iter()
.map(|location| format!(" ---------------\n {location}"))
.collect::<Vec<_>>()
.join("\n")
}
#[non_exhaustive]
#[derive(Debug)]
pub struct BorrowError {
#[cfg(debug_assertions)]
pub attempted_at: super::Location,
#[cfg(debug_assertions)]
pub already_borrowed_at: Vec<super::Location>,
}
impl std::error::Error for BorrowError {}
impl std::fmt::Display for BorrowError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
#[cfg(debug_assertions)]
{
write!(
f,
"Value is already borrowed mutably, current active borrows: \n{}\n\n",
locations_display(&self.already_borrowed_at)
)
}
#[cfg(not(debug_assertions))]
{
write!(f, "Value is already borrowed mutably")
}
}
}
impl std::error::Error for BorrowMutError {}
#[derive(Debug)]
#[non_exhaustive]
pub struct BorrowMutError {
#[cfg(debug_assertions)]
pub attempted_at: super::Location,
#[cfg(debug_assertions)]
pub already_borrowed_at: Vec<super::Location>,
}
impl std::fmt::Display for BorrowMutError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
#[cfg(debug_assertions)]
{
write!(
f,
"Value is already borrowed, current active borrows:\n{}\n\n",
locations_display(&self.already_borrowed_at)
)
}
#[cfg(not(debug_assertions))]
{
write!(f, "Value is already borrowed")
}
}
}
}
#[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 = &'static std::panic::Location<'static>;
#[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),
}
}
#[cfg_attr(debug_assertions, track_caller)]
pub fn into_inner(self) -> T {
debug_assert!(self.borrow.flag.get() == UNUSED);
self.value.into_inner()
}
}
impl<T: ?Sized> RefCell<T> {
#[cfg_attr(debug_assertions, inline(never))]
#[cfg_attr(debug_assertions, track_caller)]
pub fn borrow(&self) -> Ref<'_, T> {
match self.try_borrow() {
Ok(value) => value,
Err(message) => panic!(
"Borrowing {} immutably failed: {}",
std::any::type_name::<Self>(),
message
),
}
}
#[cfg_attr(debug_assertions, inline(never))]
#[cfg_attr(debug_assertions, track_caller)]
pub fn try_borrow(&self) -> Result<Ref<'_, T>, crate::error::BorrowError> {
match BorrowRef::new(&self.borrow) {
Some(b) => Ok(Ref {
_value: unsafe { &*self.value.get() },
_borrow: b,
}),
None => {
#[cfg(debug_assertions)]
{
Err(crate::error::BorrowError {
attempted_at: get_caller(),
already_borrowed_at: self.borrow.locations.borrow().clone(),
})
}
#[cfg(not(debug_assertions))]
{
Err(crate::error::BorrowError {})
}
}
}
}
#[cfg_attr(debug_assertions, inline(never))]
#[cfg_attr(debug_assertions, track_caller)]
pub fn borrow_mut(&self) -> RefMut<'_, T> {
match self.try_borrow_mut() {
Ok(value) => value,
Err(message) => panic!(
"Borrowing {} mutably failed: {}",
std::any::type_name::<Self>(),
message
),
}
}
#[cfg_attr(debug_assertions, inline(never))]
#[cfg_attr(debug_assertions, track_caller)]
pub fn try_borrow_mut(&self) -> Result<RefMut<'_, T>, error::BorrowMutError> {
match BorrowRefMut::new(&self.borrow) {
Some(b) => Ok(RefMut {
_value: unsafe { &mut *self.value.get() },
_borrow: b,
}),
None => {
#[cfg(debug_assertions)]
{
Err(error::BorrowMutError {
attempted_at: get_caller(),
already_borrowed_at: self.borrow.locations.borrow().clone(),
})
}
#[cfg(not(debug_assertions))]
{
Err(error::BorrowMutError {})
}
}
}
}
}
#[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 {}
#[cfg(debug_assertions)]
#[inline(never)]
#[track_caller]
fn get_caller() -> Location {
std::panic::Location::caller()
}
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(not(debug_assertions), inline)]
#[cfg_attr(debug_assertions, inline(never))]
#[cfg_attr(debug_assertions, track_caller)]
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 })
}
}
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 + 'b> Ref<'b, T> {
#[inline]
pub fn map<U: ?Sized, F>(orig: Ref<'b, T>, f: F) -> Ref<'b, U>
where
F: FnOnce(&T) -> &U,
{
Ref {
_value: f(&*orig._value),
_borrow: orig._borrow,
}
}
}
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(not(debug_assertions), inline)]
#[cfg_attr(debug_assertions, inline(never))]
#[cfg_attr(debug_assertions, track_caller)]
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 })
}
}
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 + 'b> RefMut<'b, T> {
pub fn map<U: ?Sized, F>(orig: RefMut<'b, T>, f: F) -> RefMut<'b, U>
where
F: FnOnce(&mut T) -> &mut U,
{
RefMut {
_value: f(&mut *orig._value),
_borrow: orig._borrow,
}
}
}
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);
}
#[test]
fn ok_borrows_map() {
let a = RefCell::new((0, 1));
let b = a.borrow();
let c = super::Ref::map(b, |v| &v.0);
assert_eq!(*c, 0);
assert!(a.try_borrow().is_ok());
assert!(a.try_borrow_mut().is_err());
drop(c);
assert!(a.try_borrow().is_ok());
assert!(a.try_borrow_mut().is_ok());
let mut b = a.borrow_mut();
assert_eq!(b.0, 0);
b.0 = 999;
drop(b);
assert_eq!(a.borrow().0, 999);
}
#[test]
fn ok_borrow_mut_map() {
let a = RefCell::new((0, 1));
let b = a.borrow_mut();
let mut c = super::RefMut::map(b, |v| &mut v.0);
assert_eq!(*c, 0);
*c = 999;
drop(c);
assert_eq!(a.try_borrow().unwrap().0, 999);
}
#[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();
}
}