1 //! Wrapper around Rc<> to make ownership clearer
2 //!
3 //! The idea is to have ownership represented by a SharedBox<T>.
4 //! Temporary ownership can be held using a WeakBox<T>, which should
5 //! not be held across async points. This reduces the risk of accidental
6 //! lifetime extension.
7 
8 use std::{
9     ops::Deref,
10     rc::{Rc, Weak},
11 };
12 
13 /// A Box<> where static "weak" references to the contents can be taken,
14 /// and fallibly upgraded at a later point. Unlike Rc<>, weak references
15 /// cannot be upgraded back to owning references, so ownership remains clear
16 /// and reference cycles avoided.
17 #[derive(Debug)]
18 pub struct SharedBox<T: ?Sized>(Rc<T>);
19 
20 impl<T> SharedBox<T> {
21     /// Constructor
new(t: T) -> Self22     pub fn new(t: T) -> Self {
23         Self(t.into())
24     }
25 
26     /// Constructs a new SharedBox<T> while giving you a WeakBox<T> to the allocation,
27     /// to allow you to construct a T which holds a weak pointer to itself.
new_cyclic(f: impl FnOnce(WeakBox<T>) -> T) -> Self28     pub fn new_cyclic(f: impl FnOnce(WeakBox<T>) -> T) -> Self {
29         Self(Rc::new_cyclic(|weak| f(WeakBox(weak.clone()))))
30     }
31 
32     /// Produce a weak reference to the contents
downgrade(&self) -> WeakBox<T>33     pub fn downgrade(&self) -> WeakBox<T> {
34         WeakBox(Rc::downgrade(&self.0))
35     }
36 
37     /// Produce an upgraded weak reference to the contents
as_ref(&self) -> WeakBoxRef<T>38     pub fn as_ref(&self) -> WeakBoxRef<T> {
39         WeakBoxRef(self.0.deref(), Rc::downgrade(&self.0))
40     }
41 }
42 
43 impl<T> From<T> for SharedBox<T> {
from(value: T) -> Self44     fn from(value: T) -> Self {
45         Self(value.into())
46     }
47 }
48 
49 impl<T> Deref for SharedBox<T> {
50     type Target = T;
51 
deref(&self) -> &Self::Target52     fn deref(&self) -> &Self::Target {
53         self.0.deref()
54     }
55 }
56 
57 /// A weak reference to the contents within a SharedBox<>
58 pub struct WeakBox<T: ?Sized>(Weak<T>);
59 
60 impl<T: ?Sized> WeakBox<T> {
61     /// Fallibly upgrade to a strong reference, passed into the supplied closure.
62     /// The strong reference is not passed into the closure to avoid accidental
63     /// lifetime extension.
64     ///
65     /// Note: reference-counting is used so that, if the passed-in closure drops
66     /// the SharedBox<>, the strong reference remains safe. But please don't
67     /// do that!
with<U>(&self, f: impl FnOnce(Option<WeakBoxRef<T>>) -> U) -> U68     pub fn with<U>(&self, f: impl FnOnce(Option<WeakBoxRef<T>>) -> U) -> U {
69         f(self.0.upgrade().as_deref().map(|x| WeakBoxRef(x, self.0.clone())))
70     }
71 }
72 
73 impl<T: ?Sized> Clone for WeakBox<T> {
clone(&self) -> Self74     fn clone(&self) -> Self {
75         Self(self.0.clone())
76     }
77 }
78 
79 /// A strong reference to the contents within a SharedBox<>.
80 pub struct WeakBoxRef<'a, T: ?Sized>(&'a T, Weak<T>);
81 
82 impl<'a, T: ?Sized> WeakBoxRef<'a, T> {
83     /// Downgrade to a weak reference (with static lifetime) to the contents
84     /// within the underlying SharedBox<>
downgrade(&self) -> WeakBox<T>85     pub fn downgrade(&self) -> WeakBox<T> {
86         WeakBox(self.1.clone())
87     }
88 }
89 
90 impl<'a, T: ?Sized> Deref for WeakBoxRef<'a, T> {
91     type Target = T;
92 
deref(&self) -> &Self::Target93     fn deref(&self) -> &Self::Target {
94         self.0
95     }
96 }
97 
98 impl<'a, T: ?Sized> Clone for WeakBoxRef<'a, T> {
clone(&self) -> Self99     fn clone(&self) -> Self {
100         Self(self.0, self.1.clone())
101     }
102 }
103