fp_library/classes/ref_counted_pointer.rs
1//! Reference-counted pointers with shared ownership, unwrapping, and take-cell capabilities.
2//!
3//! In addition to cloneable shared pointers, this module provides a
4//! [`TakeCellOf`](RefCountedPointer::TakeCellOf) abstraction: a cloneable cell
5//! that holds a value which can be taken exactly once. This pairs the
6//! appropriate interior mutability primitive with each pointer type
7//! (`RefCell` for `Rc`, `Mutex` for `Arc`), enabling move-out-of-closure
8//! patterns while preserving thread safety when needed.
9//!
10//! ### Examples
11//!
12//! ```
13//! use fp_library::{
14//! brands::*,
15//! functions::*,
16//! };
17//!
18//! let ptr = ref_counted_pointer_new::<RcBrand, _>(42);
19//! let clone = ptr.clone();
20//! assert_eq!(*clone, 42);
21//!
22//! let cell = take_cell_new::<RcBrand, _>(99);
23//! let cell_clone = cell.clone();
24//! assert_eq!(take_cell_take::<RcBrand, _>(&cell), Some(99));
25//! assert_eq!(take_cell_take::<RcBrand, _>(&cell_clone), None);
26//! ```
27
28#[fp_macros::document_module]
29mod inner {
30 use {
31 fp_macros::*,
32 std::ops::Deref,
33 };
34
35 /// Trait for reference-counted pointers with shared ownership.
36 ///
37 /// Provides [`Of`](Self::Of) (a cloneable, dereferenceable pointer)
38 /// and [`TakeCellOf`](Self::TakeCellOf) (a cloneable cell supporting one-shot value
39 /// extraction). The latter pairs the pointer with an appropriate interior mutability
40 /// primitive (`RefCell` for `Rc`, `Mutex` for `Arc`).
41 ///
42 /// This is an independent trait (not a supertrait of
43 /// [`Pointer`](crate::classes::Pointer)). Both `RcBrand` and `ArcBrand`
44 /// implement `Pointer` and `RefCountedPointer` independently.
45 pub trait RefCountedPointer {
46 /// The cloneable pointer type constructor.
47 ///
48 /// For Rc/Arc, this is the same as `Of<'a, T>`.
49 type Of<'a, T: ?Sized + 'a>: Clone + Deref<Target = T> + 'a;
50
51 /// Wraps a sized value in a cloneable pointer.
52 #[document_signature]
53 ///
54 #[document_type_parameters("The lifetime of the value.", "The type of the value to wrap.")]
55 ///
56 #[document_parameters("The value to wrap.")]
57 ///
58 #[document_returns("The value wrapped in the cloneable pointer type.")]
59 #[document_examples]
60 ///
61 /// ```
62 /// use fp_library::{
63 /// brands::*,
64 /// functions::*,
65 /// };
66 ///
67 /// let ptr = ref_counted_pointer_new::<RcBrand, _>(42);
68 /// assert_eq!(*ptr, 42);
69 /// ```
70 fn new<'a, T: 'a>(value: T) -> Self::Of<'a, T>
71 where
72 Self::Of<'a, T>: Sized;
73
74 /// Attempts to unwrap the inner value if this is the sole reference.
75 #[document_signature]
76 ///
77 #[document_type_parameters(
78 "The lifetime of the wrapped value.",
79 "The type of the wrapped value."
80 )]
81 ///
82 #[document_parameters("The pointer to attempt to unwrap.")]
83 ///
84 #[document_returns("`Ok(value)` if this is the sole reference, otherwise `Err(ptr)`.")]
85 #[document_examples]
86 ///
87 /// ```
88 /// use fp_library::{
89 /// brands::*,
90 /// functions::*,
91 /// };
92 ///
93 /// let ptr = ref_counted_pointer_new::<RcBrand, _>(42);
94 /// assert_eq!(try_unwrap::<RcBrand, _>(ptr), Ok(42));
95 ///
96 /// let ptr1 = ref_counted_pointer_new::<RcBrand, _>(42);
97 /// let ptr2 = ptr1.clone();
98 /// assert!(try_unwrap::<RcBrand, _>(ptr1).is_err());
99 /// ```
100 fn try_unwrap<'a, T: 'a>(ptr: Self::Of<'a, T>) -> Result<T, Self::Of<'a, T>>;
101
102 /// A cloneable cell that holds an optional value which can be taken exactly once.
103 ///
104 /// For [`RcBrand`](crate::brands::RcBrand), this is `Rc<RefCell<Option<T>>>`.
105 /// For [`ArcBrand`](crate::brands::ArcBrand), this is `Arc<Mutex<Option<T>>>`.
106 type TakeCellOf<'a, T: 'a>: Clone + 'a;
107
108 /// Creates a new take-cell containing the given value.
109 #[document_signature]
110 ///
111 #[document_type_parameters("The lifetime of the value.", "The type of the value to store.")]
112 ///
113 #[document_parameters("The value to store in the cell.")]
114 ///
115 #[document_returns("A new take-cell containing the value.")]
116 #[document_examples]
117 ///
118 /// ```
119 /// use fp_library::{
120 /// brands::*,
121 /// functions::*,
122 /// };
123 ///
124 /// let cell = take_cell_new::<RcBrand, _>(42);
125 /// assert_eq!(take_cell_take::<RcBrand, _>(&cell), Some(42));
126 /// ```
127 fn take_cell_new<'a, T: 'a>(value: T) -> Self::TakeCellOf<'a, T>;
128
129 /// Takes the value out of the cell, leaving `None` behind.
130 ///
131 /// Returns `Some(value)` the first time, `None` on subsequent calls.
132 #[document_signature]
133 ///
134 #[document_type_parameters("The lifetime of the value.", "The type of the stored value.")]
135 ///
136 #[document_parameters("The cell to take the value from.")]
137 ///
138 #[document_returns("`Some(value)` if the cell still contains a value, `None` otherwise.")]
139 #[document_examples]
140 ///
141 /// ```
142 /// use fp_library::{
143 /// brands::*,
144 /// functions::*,
145 /// };
146 ///
147 /// let cell = take_cell_new::<RcBrand, _>(42);
148 /// assert_eq!(take_cell_take::<RcBrand, _>(&cell), Some(42));
149 /// assert_eq!(take_cell_take::<RcBrand, _>(&cell), None);
150 /// ```
151 fn take_cell_take<'a, T: 'a>(cell: &Self::TakeCellOf<'a, T>) -> Option<T>;
152 }
153
154 /// Attempts to unwrap the inner value if this is the sole reference.
155 ///
156 /// Free function version that dispatches to [the type class' associated function][`RefCountedPointer::try_unwrap`].
157 #[document_signature]
158 ///
159 #[document_type_parameters(
160 "The pointer brand.",
161 "The lifetime of the wrapped value.",
162 "The type of the wrapped value."
163 )]
164 ///
165 #[document_parameters("The pointer to attempt to unwrap.")]
166 ///
167 #[document_returns("`Ok(value)` if this is the sole reference, otherwise `Err(ptr)`.")]
168 #[document_examples]
169 ///
170 /// ```
171 /// use fp_library::{
172 /// brands::*,
173 /// functions::*,
174 /// };
175 ///
176 /// let ptr = ref_counted_pointer_new::<RcBrand, _>(42);
177 /// assert_eq!(try_unwrap::<RcBrand, _>(ptr), Ok(42));
178 ///
179 /// let ptr1 = ref_counted_pointer_new::<RcBrand, _>(42);
180 /// let ptr2 = ptr1.clone();
181 /// assert!(try_unwrap::<RcBrand, _>(ptr1).is_err());
182 /// ```
183 pub fn try_unwrap<'a, P: RefCountedPointer, T: 'a>(
184 ptr: P::Of<'a, T>
185 ) -> Result<T, P::Of<'a, T>> {
186 P::try_unwrap(ptr)
187 }
188
189 /// Wraps a sized value in a cloneable pointer.
190 #[document_signature]
191 ///
192 #[document_type_parameters(
193 "The pointer brand.",
194 "The lifetime of the value.",
195 "The type of the value to wrap."
196 )]
197 ///
198 #[document_parameters("The value to wrap.")]
199 ///
200 #[document_returns("The value wrapped in the cloneable pointer type.")]
201 #[document_examples]
202 ///
203 /// ```
204 /// use fp_library::{
205 /// brands::*,
206 /// classes::*,
207 /// functions::*,
208 /// };
209 ///
210 /// let ptr = ref_counted_pointer_new::<RcBrand, _>(42);
211 /// let clone = ptr.clone();
212 /// assert_eq!(*clone, 42);
213 /// ```
214 pub fn new<'a, P: RefCountedPointer, T: 'a>(value: T) -> P::Of<'a, T>
215 where
216 P::Of<'a, T>: Sized, {
217 P::new(value)
218 }
219
220 /// Creates a new take-cell containing the given value.
221 ///
222 /// Free function version that dispatches to [the type class' associated function][`RefCountedPointer::take_cell_new`].
223 #[document_signature]
224 ///
225 #[document_type_parameters(
226 "The pointer brand.",
227 "The lifetime of the value.",
228 "The type of the value to store."
229 )]
230 ///
231 #[document_parameters("The value to store in the cell.")]
232 ///
233 #[document_returns("A new take-cell containing the value.")]
234 #[document_examples]
235 ///
236 /// ```
237 /// use fp_library::{
238 /// brands::*,
239 /// functions::*,
240 /// };
241 ///
242 /// let cell = take_cell_new::<RcBrand, _>(42);
243 /// assert_eq!(take_cell_take::<RcBrand, _>(&cell), Some(42));
244 /// ```
245 pub fn take_cell_new<'a, P: RefCountedPointer, T: 'a>(value: T) -> P::TakeCellOf<'a, T> {
246 P::take_cell_new(value)
247 }
248
249 /// Takes the value out of a take-cell, leaving `None` behind.
250 ///
251 /// Free function version that dispatches to [the type class' associated function][`RefCountedPointer::take_cell_take`].
252 #[document_signature]
253 ///
254 #[document_type_parameters(
255 "The pointer brand.",
256 "The lifetime of the value.",
257 "The type of the stored value."
258 )]
259 ///
260 #[document_parameters("The cell to take the value from.")]
261 ///
262 #[document_returns("`Some(value)` if the cell still contains a value, `None` otherwise.")]
263 #[document_examples]
264 ///
265 /// ```
266 /// use fp_library::{
267 /// brands::*,
268 /// functions::*,
269 /// };
270 ///
271 /// let cell = take_cell_new::<RcBrand, _>(42);
272 /// assert_eq!(take_cell_take::<RcBrand, _>(&cell), Some(42));
273 /// assert_eq!(take_cell_take::<RcBrand, _>(&cell), None);
274 /// ```
275 pub fn take_cell_take<'a, P: RefCountedPointer, T: 'a>(
276 cell: &P::TakeCellOf<'a, T>
277 ) -> Option<T> {
278 P::take_cell_take(cell)
279 }
280}
281
282pub use inner::*;