Skip to main content

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::*;