1use std::{
38 cell::{BorrowError, BorrowMutError, Ref, RefCell, RefMut},
39 hash::{Hash, Hasher},
40 rc::{Rc, Weak},
41};
42
43#[repr(transparent)]
45#[derive(Debug)]
46pub struct SharedCell<T>(Rc<RefCell<T>>);
47
48impl<T> Clone for SharedCell<T> {
49 fn clone(&self) -> Self {
50 Self(self.0.clone())
51 }
52}
53
54impl<T> SharedCell<T> {
55 #[inline]
57 pub fn new(value: T) -> Self {
58 Self(Rc::new(RefCell::new(value)))
59 }
60
61 #[inline]
63 #[must_use]
64 pub fn downgrade(&self) -> WeakCell<T> {
65 WeakCell(Rc::downgrade(&self.0))
66 }
67
68 #[inline]
70 #[must_use]
71 pub fn borrow(&self) -> Ref<'_, T> {
72 self.0.borrow()
73 }
74
75 #[inline]
77 #[must_use]
78 pub fn borrow_mut(&self) -> RefMut<'_, T> {
79 self.0.borrow_mut()
80 }
81
82 #[inline]
88 pub fn try_borrow(&self) -> Result<Ref<'_, T>, BorrowError> {
89 self.0.try_borrow()
90 }
91
92 #[inline]
99 pub fn try_borrow_mut(&self) -> Result<RefMut<'_, T>, BorrowMutError> {
100 self.0.try_borrow_mut()
101 }
102
103 #[inline]
105 #[must_use]
106 pub fn strong_count(&self) -> usize {
107 Rc::strong_count(&self.0)
108 }
109
110 #[inline]
112 #[must_use]
113 pub fn weak_count(&self) -> usize {
114 Rc::weak_count(&self.0)
115 }
116
117 #[inline]
119 #[must_use]
120 pub fn as_ptr(&self) -> *const RefCell<T> {
121 Rc::as_ptr(&self.0)
122 }
123
124 #[inline]
129 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
130 f(&self.0.borrow())
131 }
132
133 #[inline]
138 pub fn with_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
139 f(&mut self.0.borrow_mut())
140 }
141}
142
143impl<T> PartialEq for SharedCell<T> {
144 fn eq(&self, other: &Self) -> bool {
146 Rc::ptr_eq(&self.0, &other.0)
147 }
148}
149
150impl<T> Eq for SharedCell<T> {}
151
152impl<T> Hash for SharedCell<T> {
153 fn hash<H: Hasher>(&self, state: &mut H) {
155 Rc::as_ptr(&self.0).hash(state);
156 }
157}
158
159impl<T> From<Rc<RefCell<T>>> for SharedCell<T> {
160 fn from(inner: Rc<RefCell<T>>) -> Self {
161 Self(inner)
162 }
163}
164
165impl<T> From<SharedCell<T>> for Rc<RefCell<T>> {
166 fn from(shared: SharedCell<T>) -> Self {
167 shared.0
168 }
169}
170
171impl<T> std::ops::Deref for SharedCell<T> {
172 type Target = Rc<RefCell<T>>;
173
174 fn deref(&self) -> &Self::Target {
175 &self.0
176 }
177}
178
179#[repr(transparent)]
181#[derive(Debug)]
182pub struct WeakCell<T>(Weak<RefCell<T>>);
183
184impl<T> Clone for WeakCell<T> {
185 fn clone(&self) -> Self {
186 Self(self.0.clone())
187 }
188}
189
190impl<T> WeakCell<T> {
191 #[inline]
193 pub fn upgrade(&self) -> Option<SharedCell<T>> {
194 self.0.upgrade().map(SharedCell)
195 }
196
197 #[inline]
199 #[must_use]
200 pub fn is_dropped(&self) -> bool {
201 self.0.strong_count() == 0
202 }
203}
204
205impl<T> From<Weak<RefCell<T>>> for WeakCell<T> {
206 fn from(inner: Weak<RefCell<T>>) -> Self {
207 Self(inner)
208 }
209}
210
211impl<T> From<WeakCell<T>> for Weak<RefCell<T>> {
212 fn from(cell: WeakCell<T>) -> Self {
213 cell.0
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use std::collections::HashSet;
220
221 use rstest::rstest;
222
223 use super::*;
224
225 #[rstest]
226 fn test_shared_cell_new_and_borrow() {
227 let cell = SharedCell::new(42);
228 assert_eq!(*cell.borrow(), 42);
229 }
230
231 #[rstest]
232 fn test_shared_cell_borrow_mut() {
233 let cell = SharedCell::new(0);
234 *cell.borrow_mut() = 99;
235 assert_eq!(*cell.borrow(), 99);
236 }
237
238 #[rstest]
239 fn test_shared_cell_clone_shares_value() {
240 let cell = SharedCell::new(10);
241 let clone = cell.clone();
242 *cell.borrow_mut() = 20;
243 assert_eq!(*clone.borrow(), 20);
244 }
245
246 #[rstest]
247 fn test_shared_cell_strong_weak_counts() {
248 let cell = SharedCell::new(1);
249 assert_eq!(cell.strong_count(), 1);
250 assert_eq!(cell.weak_count(), 0);
251
252 let weak = cell.downgrade();
253 assert_eq!(cell.weak_count(), 1);
254 assert_eq!(cell.strong_count(), 1);
255
256 let clone = cell.clone();
257 assert_eq!(cell.strong_count(), 2);
258 drop(clone);
259 assert_eq!(cell.strong_count(), 1);
260 drop(weak);
261 assert_eq!(cell.weak_count(), 0);
262 }
263
264 #[rstest]
265 fn test_weak_cell_upgrade_succeeds_while_alive() {
266 let cell = SharedCell::new(10);
267 let weak = cell.downgrade();
268 assert!(!weak.is_dropped());
269
270 let upgraded = weak.upgrade();
271 assert!(upgraded.is_some());
272 assert_eq!(*upgraded.unwrap().borrow(), 10);
273 }
274
275 #[rstest]
276 fn test_weak_cell_upgrade_fails_after_drop() {
277 let weak = {
278 let cell = SharedCell::new(10);
279 cell.downgrade()
280 };
281 assert!(weak.is_dropped());
282 assert!(weak.upgrade().is_none());
283 }
284
285 #[rstest]
286 #[expect(clippy::redundant_clone, reason = "Clone is the behavior under test")]
287 fn test_weak_cell_clone() {
288 let cell = SharedCell::new(5);
289 let weak1 = cell.downgrade();
290 let weak2 = weak1.clone();
291 assert_eq!(cell.weak_count(), 2);
292 assert_eq!(*weak2.upgrade().unwrap().borrow(), 5);
293 }
294
295 #[rstest]
296 fn test_try_borrow_fails_while_mutably_borrowed() {
297 let cell = SharedCell::new(0);
298 let _guard = cell.borrow_mut();
299 assert!(cell.try_borrow().is_err());
300 }
301
302 #[rstest]
303 fn test_try_borrow_mut_fails_while_borrowed() {
304 let cell = SharedCell::new(0);
305 let _guard = cell.borrow();
306 assert!(cell.try_borrow_mut().is_err());
307 }
308
309 #[rstest]
310 fn test_from_rc_refcell_roundtrip() {
311 let rc = Rc::new(RefCell::new(5));
312 let cell = SharedCell::from(rc);
313 assert_eq!(*cell.borrow(), 5);
314
315 let back: Rc<RefCell<i32>> = cell.into();
316 assert_eq!(*back.borrow(), 5);
317 }
318
319 #[rstest]
320 fn test_from_weak_refcell_roundtrip() {
321 let rc = Rc::new(RefCell::new(7));
322 let weak_cell = WeakCell::from(Rc::downgrade(&rc));
323 assert_eq!(*weak_cell.upgrade().unwrap().borrow(), 7);
324
325 let back: Weak<RefCell<i32>> = weak_cell.into();
326 assert_eq!(*back.upgrade().unwrap().borrow(), 7);
327 }
328
329 #[rstest]
330 fn test_partial_eq_is_pointer_identity() {
331 let a = SharedCell::new(10);
332 let b = a.clone();
333 let c = SharedCell::new(10);
334
335 assert_eq!(a, b);
336 assert_ne!(a, c);
337 }
338
339 #[rstest]
340 #[expect(
341 clippy::mutable_key_type,
342 reason = "SharedCell hashes by pointer identity, not interior value"
343 )]
344 fn test_hash_matches_pointer_identity() {
345 let a = SharedCell::new(10);
346 let b = a.clone();
347 let c = SharedCell::new(10);
348
349 let mut set: HashSet<SharedCell<i32>> = HashSet::new();
350 set.insert(a);
351 assert!(set.contains(&b));
352 assert!(!set.contains(&c));
353 }
354
355 #[rstest]
356 fn test_as_ptr_matches_clone() {
357 let cell = SharedCell::new(0);
358 let cloned = cell.clone();
359 assert_eq!(cell.as_ptr(), cloned.as_ptr());
360 }
361
362 #[rstest]
363 fn test_with_drops_borrow_before_returning() {
364 let cell = SharedCell::new(100);
365 let value = cell.with(|v| *v);
366
367 *cell.borrow_mut() = 1;
369
370 assert_eq!(value, 100);
371 assert_eq!(*cell.borrow(), 1);
372 }
373
374 #[rstest]
375 fn test_with_mut_drops_borrow_before_returning() {
376 let cell = SharedCell::new(0);
377 let returned = cell.with_mut(|v| {
378 *v = 7;
379 *v
380 });
381
382 assert_eq!(returned, 7);
384 assert_eq!(*cell.borrow(), 7);
385 }
386}