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