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