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