tinystd/refcell.rs
1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5// http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12use std::{
13 cell::{Cell, UnsafeCell},
14 ops::{Deref, DerefMut},
15};
16
17/// References can exist in one of three states:
18/// - `Unshared` means the reference has not been given out at all
19/// - `Exclusive` means exactly one owner has the reference
20/// - `Shared(n)` means the reference has been given out to `n` borrowers
21#[derive(Copy, Clone)]
22pub enum RefState {
23 Unshared,
24 Exclusive,
25 Shared(usize),
26}
27
28/// RefCell is a **non** thread-safe container for T
29pub struct RefCell<T> {
30 /// UnsafeCell is needed as it is the core interior mutability type
31 value: UnsafeCell<T>,
32 /// RefState is wrapped in a std::cell::Cell to allow mutation via &Self
33 state: Cell<RefState>,
34}
35
36impl<T> RefCell<T> {
37 /// wrap `T` in a reference cell
38 pub fn from(value: T) -> Self {
39 Self {
40 value: UnsafeCell::from(value),
41 state: Cell::from(RefState::Unshared),
42 }
43 }
44
45 /// Return an immutable shared reference to `T`, IFF there exists no other
46 /// _exclusive_ references to `T` already given out
47 pub fn borrow(&self) -> Option<Ref<'_, T>> {
48 match self.state.get() {
49 RefState::Shared(n) => {
50 self.state.set(RefState::Shared(n + 1));
51 Some(Ref { refcell: self })
52 }
53 RefState::Unshared => {
54 self.state.set(RefState::Shared(1));
55 Some(Ref { refcell: self })
56 }
57 RefState::Exclusive => None,
58 }
59 }
60
61 /// Return a **mutable** shared reference to `T`, IFF there exists no other
62 /// _exclusive_ references to `T` already given out
63 pub fn borrow_mut(&self) -> Option<RefMut<'_, T>> {
64 if let RefState::Unshared = self.state.get() {
65 self.state.set(RefState::Exclusive);
66 Some(RefMut { refcell: self })
67 } else {
68 None
69 }
70 }
71}
72/// Ref is a custom wrapper that provides the correct `Drop` impl for T, as
73/// opposed to simply returning `&T` or `&mut T`
74pub struct Ref<'refcell, T> {
75 refcell: &'refcell RefCell<T>,
76}
77
78/// Explain the drop semantics for `RefMut<T>`
79impl<T> Drop for Ref<'_, T> {
80 fn drop(&mut self) {
81 match self.refcell.state.get() {
82 RefState::Exclusive => unreachable!(),
83 RefState::Unshared => unreachable!(),
84 RefState::Shared(1) => {
85 self.refcell.state.set(RefState::Unshared);
86 }
87 RefState::Shared(n) => {
88 self.refcell.state.set(RefState::Shared(n - 1));
89 }
90 }
91 }
92}
93
94/// Explain how to dereference a `T` from an `Ref<T>`
95impl<T> Deref for Ref<'_, T> {
96 type Target = T;
97
98 /// This effectively is what makes `Ref` a smart pointer; the type is able
99 /// to correcty `Drop` or hand out the shared reference to its interior
100 fn deref(&self) -> &Self::Target {
101 unsafe { &*self.refcell.value.get() }
102 }
103}
104
105/// A `Mut`able `Ref`erence to `T`
106pub struct RefMut<'refcell, T> {
107 refcell: &'refcell RefCell<T>,
108}
109
110/// Explain the drop semantics for `RefMut<T>`
111impl<T> Drop for RefMut<'_, T> {
112 fn drop(&mut self) {
113 match self.refcell.state.get() {
114 RefState::Exclusive => {}
115 RefState::Shared(_) | RefState::Unshared => unreachable!(),
116 }
117 }
118}
119
120/// Explain how to _immutably_ dereference a `T` from an `RefMut<T>`
121impl<T> Deref for RefMut<'_, T> {
122 type Target = T;
123
124 /// This effectively is what makes `Ref` a smart pointer; the type is able
125 /// to correcty `Drop` or hand out the shared reference to its interior
126 fn deref(&self) -> &Self::Target {
127 unsafe { &*self.refcell.value.get() }
128 }
129}
130
131/// Explain how to _mutably_ dereference a `T` from an `RefMut<T>`
132impl<T> DerefMut for RefMut<'_, T> {
133 fn deref_mut(&mut self) -> &mut Self::Target {
134 unsafe { &mut *self.refcell.value.get() }
135 }
136}