Skip to main content

fp_library/types/
arc_ptr.rs

1//! Thread-safe reference-counted pointer abstraction using [`Arc`](std::sync::Arc).
2//!
3//! Provides trait implementations for using `Arc` in the library's pointer abstraction hierarchy.
4//!
5//! ### Examples
6//!
7//! ```
8//! use fp_library::{
9//! 	brands::*,
10//! 	functions::*,
11//! };
12//!
13//! let ptr = send_ref_counted_pointer_new::<ArcBrand, _>(42);
14//! assert_eq!(*ptr, 42);
15//! ```
16
17#[fp_macros::document_module]
18mod inner {
19	use {
20		crate::{
21			brands::ArcBrand,
22			classes::{
23				Pointer,
24				RefCountedPointer,
25				SendRefCountedPointer,
26				SendUnsizedCoercible,
27				UnsizedCoercible,
28			},
29		},
30		fp_macros::*,
31		std::sync::{
32			Arc,
33			Mutex,
34		},
35	};
36
37	impl Pointer for ArcBrand {
38		type Of<'a, T: ?Sized + 'a> = Arc<T>;
39
40		/// Wraps a sized value in an `Arc`.
41		#[document_signature]
42		///
43		#[document_type_parameters("The lifetime of the value.", "The type of the value to wrap.")]
44		///
45		#[document_parameters("The value to wrap.")]
46		///
47		#[document_returns("The value wrapped in an `Arc`.")]
48		///
49		#[document_examples]
50		///
51		/// ```
52		/// use fp_library::{
53		/// 	brands::*,
54		/// 	functions::*,
55		/// };
56		///
57		/// let ptr = pointer_new::<ArcBrand, _>(42);
58		/// assert_eq!(*ptr, 42);
59		/// ```
60		fn new<'a, T: 'a>(value: T) -> Arc<T> {
61			Arc::new(value)
62		}
63	}
64
65	impl RefCountedPointer for ArcBrand {
66		type CloneableOf<'a, T: ?Sized + 'a> = Arc<T>;
67		type TakeCellOf<'a, T: 'a> = Arc<Mutex<Option<T>>>;
68
69		/// Wraps a sized value in an `Arc`.
70		#[document_signature]
71		///
72		#[document_type_parameters("The lifetime of the value.", "The type of the value to wrap.")]
73		///
74		#[document_parameters("The value to wrap.")]
75		///
76		#[document_returns("The value wrapped in an `Arc`.")]
77		///
78		#[document_examples]
79		///
80		/// ```
81		/// use fp_library::{
82		/// 	brands::*,
83		/// 	functions::*,
84		/// };
85		///
86		/// let ptr = ref_counted_pointer_new::<ArcBrand, _>(42);
87		/// assert_eq!(*ptr, 42);
88		/// ```
89		fn cloneable_new<'a, T: 'a>(value: T) -> Arc<T> {
90			Arc::new(value)
91		}
92
93		/// Attempts to unwrap the inner value if this is the sole reference.
94		#[document_signature]
95		///
96		#[document_type_parameters(
97			"The lifetime of the wrapped value.",
98			"The type of the wrapped value."
99		)]
100		///
101		#[document_parameters("The pointer to attempt to unwrap.")]
102		///
103		#[document_returns("`Ok(value)` if this is the sole reference, otherwise `Err(ptr)`.")]
104		///
105		#[document_examples]
106		///
107		/// ```
108		/// use fp_library::{
109		/// 	brands::*,
110		/// 	functions::*,
111		/// };
112		///
113		/// let ptr = ref_counted_pointer_new::<ArcBrand, _>(42);
114		/// assert_eq!(try_unwrap::<ArcBrand, _>(ptr), Ok(42));
115		/// ```
116		fn try_unwrap<'a, T: 'a>(ptr: Arc<T>) -> Result<T, Arc<T>> {
117			Arc::try_unwrap(ptr)
118		}
119
120		/// Creates a new take-cell containing the given value.
121		#[document_signature]
122		///
123		#[document_type_parameters("The lifetime of the value.", "The type of the value to store.")]
124		///
125		#[document_parameters("The value to store in the cell.")]
126		///
127		#[document_returns("A new `Arc<Mutex<Option<T>>>` containing the value.")]
128		///
129		#[document_examples]
130		///
131		/// ```
132		/// use fp_library::{
133		/// 	brands::*,
134		/// 	classes::*,
135		/// };
136		///
137		/// let cell = ArcBrand::take_cell_new(42);
138		/// assert_eq!(ArcBrand::take_cell_take(&cell), Some(42));
139		/// ```
140		fn take_cell_new<'a, T: 'a>(value: T) -> Arc<Mutex<Option<T>>> {
141			Arc::new(Mutex::new(Some(value)))
142		}
143
144		/// Takes the value out of the cell, leaving `None` behind.
145		#[document_signature]
146		///
147		#[document_type_parameters("The lifetime of the value.", "The type of the stored value.")]
148		///
149		#[document_parameters("The cell to take the value from.")]
150		///
151		#[document_returns("`Some(value)` if the cell still contains a value, `None` otherwise.")]
152		///
153		#[document_examples]
154		///
155		/// ```
156		/// use fp_library::{
157		/// 	brands::*,
158		/// 	classes::*,
159		/// };
160		///
161		/// let cell = ArcBrand::take_cell_new(42);
162		/// assert_eq!(ArcBrand::take_cell_take(&cell), Some(42));
163		/// assert_eq!(ArcBrand::take_cell_take(&cell), None);
164		/// ```
165		fn take_cell_take<'a, T: 'a>(cell: &Arc<Mutex<Option<T>>>) -> Option<T> {
166			// SAFETY: lock only fails on poisoning, which cannot occur here
167			#[allow(clippy::unwrap_used)]
168			cell.lock().unwrap().take()
169		}
170	}
171
172	impl SendRefCountedPointer for ArcBrand {
173		type SendOf<'a, T: ?Sized + Send + Sync + 'a> = Arc<T>;
174
175		/// Wraps a sized value in an `Arc`.
176		#[document_signature]
177		///
178		#[document_type_parameters("The lifetime of the value.", "The type of the value to wrap.")]
179		///
180		#[document_parameters("The value to wrap.")]
181		///
182		#[document_returns("The value wrapped in an `Arc`.")]
183		///
184		#[document_examples]
185		///
186		/// ```
187		/// use fp_library::{
188		/// 	brands::*,
189		/// 	functions::*,
190		/// };
191		///
192		/// let ptr = send_ref_counted_pointer_new::<ArcBrand, _>(42);
193		/// assert_eq!(*ptr, 42);
194		/// ```
195		fn send_new<'a, T: Send + Sync + 'a>(value: T) -> Arc<T> {
196			Arc::new(value)
197		}
198	}
199
200	impl UnsizedCoercible for ArcBrand {
201		/// Coerces a sized closure to a `dyn Fn` wrapped in an `Arc`.
202		#[document_signature]
203		///
204		#[document_type_parameters(
205			"The lifetime of the closure.",
206			"The input type of the function.",
207			"The output type of the function."
208		)]
209		///
210		#[document_parameters("The closure to coerce.")]
211		///
212		#[document_returns("The closure wrapped in an `Arc` as a trait object.")]
213		///
214		#[document_examples]
215		///
216		/// ```
217		/// use fp_library::{
218		/// 	brands::*,
219		/// 	functions::*,
220		/// };
221		///
222		/// let f = coerce_fn::<ArcBrand, _, _>(|x: i32| x + 1);
223		/// assert_eq!(f(1), 2);
224		/// ```
225		fn coerce_fn<'a, A: 'a, B: 'a>(f: impl 'a + Fn(A) -> B) -> Arc<dyn 'a + Fn(A) -> B> {
226			Arc::new(f)
227		}
228	}
229
230	impl SendUnsizedCoercible for ArcBrand {
231		/// Coerces a sized Send+Sync closure to a `dyn Fn + Send + Sync` wrapped in an `Arc`.
232		#[document_signature]
233		///
234		#[document_type_parameters(
235			"The lifetime of the closure.",
236			"The input type of the function.",
237			"The output type of the function."
238		)]
239		///
240		#[document_parameters("The closure to coerce.")]
241		///
242		#[document_returns("The closure wrapped in an `Arc` as a thread-safe trait object.")]
243		///
244		#[document_examples]
245		///
246		/// ```
247		/// use fp_library::{
248		/// 	brands::*,
249		/// 	functions::*,
250		/// };
251		///
252		/// let f = coerce_send_fn::<ArcBrand, _, _>(|x: i32| x + 1);
253		/// assert_eq!(f(1), 2);
254		/// ```
255		fn coerce_send_fn<'a, A: 'a, B: 'a>(
256			f: impl 'a + Fn(A) -> B + Send + Sync
257		) -> Arc<dyn 'a + Fn(A) -> B + Send + Sync> {
258			Arc::new(f)
259		}
260	}
261}
262
263#[cfg(test)]
264mod tests {
265	use crate::{
266		brands::ArcBrand,
267		classes::{
268			RefCountedPointer,
269			pointer::new,
270			ref_counted_pointer::cloneable_new,
271			send_ref_counted_pointer::send_new,
272		},
273	};
274
275	/// Tests that `pointer_new` correctly creates an `Arc` wrapping the value.
276	#[test]
277	fn test_arc_new() {
278		let ptr = new::<ArcBrand, _>(42);
279		assert_eq!(*ptr, 42);
280	}
281
282	/// Tests that `ref_counted_pointer_new` correctly creates an `Arc` wrapping the value.
283	#[test]
284	fn test_arc_cloneable_new() {
285		let ptr = cloneable_new::<ArcBrand, _>(42);
286		assert_eq!(*ptr, 42);
287	}
288
289	/// Tests that `send_ref_counted_pointer_new` correctly creates an `Arc` wrapping the value.
290	#[test]
291	fn test_arc_send_new() {
292		let ptr = send_new::<ArcBrand, _>(42);
293		assert_eq!(*ptr, 42);
294	}
295
296	/// Tests that cloning the pointer works as expected (shared ownership).
297	#[test]
298	fn test_arc_clone() {
299		let ptr = cloneable_new::<ArcBrand, _>(42);
300		let clone = ptr.clone();
301		assert_eq!(*clone, 42);
302	}
303
304	/// Tests `try_unwrap` behavior:
305	/// - Returns `Ok(value)` when there is only one reference.
306	/// - Returns `Err(ptr)` when there are multiple references.
307	#[test]
308	fn test_arc_try_unwrap() {
309		let ptr = cloneable_new::<ArcBrand, _>(42);
310		assert_eq!(ArcBrand::try_unwrap(ptr), Ok(42));
311
312		let ptr = cloneable_new::<ArcBrand, _>(42);
313		let _clone = ptr.clone();
314		assert!(ArcBrand::try_unwrap(ptr).is_err());
315	}
316}