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. The corresponding brand is [`ArcBrand`](crate::brands::ArcBrand).
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				ToDynCloneFn,
27				ToDynFn,
28				ToDynSendFn,
29			},
30		},
31		fp_macros::*,
32		std::sync::{
33			Arc,
34			Mutex,
35		},
36	};
37
38	impl Pointer for ArcBrand {
39		type Of<'a, T: ?Sized + 'a> = Arc<T>;
40
41		/// Wraps a sized value in an `Arc`.
42		#[document_signature]
43		///
44		#[document_type_parameters("The lifetime of the value.", "The type of the value to wrap.")]
45		///
46		#[document_parameters("The value to wrap.")]
47		///
48		#[document_returns("The value wrapped in an `Arc`.")]
49		///
50		#[document_examples]
51		///
52		/// ```
53		/// use fp_library::{
54		/// 	brands::*,
55		/// 	functions::*,
56		/// };
57		///
58		/// let ptr = pointer_new::<ArcBrand, _>(42);
59		/// assert_eq!(*ptr, 42);
60		/// ```
61		fn new<'a, T: 'a>(value: T) -> Arc<T> {
62			Arc::new(value)
63		}
64	}
65
66	impl RefCountedPointer for ArcBrand {
67		type Of<'a, T: ?Sized + 'a> = Arc<T>;
68		type TakeCellOf<'a, T: 'a> = Arc<Mutex<Option<T>>>;
69
70		/// Wraps a sized value in an `Arc`.
71		#[document_signature]
72		///
73		#[document_type_parameters("The lifetime of the value.", "The type of the value to wrap.")]
74		///
75		#[document_parameters("The value to wrap.")]
76		///
77		#[document_returns("The value wrapped in an `Arc`.")]
78		///
79		#[document_examples]
80		///
81		/// ```
82		/// use fp_library::{
83		/// 	brands::*,
84		/// 	functions::*,
85		/// };
86		///
87		/// let ptr = ref_counted_pointer_new::<ArcBrand, _>(42);
88		/// assert_eq!(*ptr, 42);
89		/// ```
90		fn new<'a, T: 'a>(value: T) -> Arc<T> {
91			Arc::new(value)
92		}
93
94		/// Attempts to unwrap the inner value if this is the sole reference.
95		#[document_signature]
96		///
97		#[document_type_parameters(
98			"The lifetime of the wrapped value.",
99			"The type of the wrapped value."
100		)]
101		///
102		#[document_parameters("The pointer to attempt to unwrap.")]
103		///
104		#[document_returns("`Ok(value)` if this is the sole reference, otherwise `Err(ptr)`.")]
105		///
106		#[document_examples]
107		///
108		/// ```
109		/// use fp_library::{
110		/// 	brands::*,
111		/// 	functions::*,
112		/// };
113		///
114		/// let ptr = ref_counted_pointer_new::<ArcBrand, _>(42);
115		/// assert_eq!(try_unwrap::<ArcBrand, _>(ptr), Ok(42));
116		/// ```
117		fn try_unwrap<'a, T: 'a>(ptr: Arc<T>) -> Result<T, Arc<T>> {
118			Arc::try_unwrap(ptr)
119		}
120
121		/// Creates a new take-cell containing the given value.
122		#[document_signature]
123		///
124		#[document_type_parameters("The lifetime of the value.", "The type of the value to store.")]
125		///
126		#[document_parameters("The value to store in the cell.")]
127		///
128		#[document_returns("A new `Arc<Mutex<Option<T>>>` containing the value.")]
129		///
130		#[document_examples]
131		///
132		/// ```
133		/// use fp_library::{
134		/// 	brands::*,
135		/// 	classes::*,
136		/// };
137		///
138		/// let cell = ArcBrand::take_cell_new(42);
139		/// assert_eq!(ArcBrand::take_cell_take(&cell), Some(42));
140		/// ```
141		fn take_cell_new<'a, T: 'a>(value: T) -> Arc<Mutex<Option<T>>> {
142			Arc::new(Mutex::new(Some(value)))
143		}
144
145		/// Takes the value out of the cell, leaving `None` behind.
146		#[document_signature]
147		///
148		#[document_type_parameters("The lifetime of the value.", "The type of the stored value.")]
149		///
150		#[document_parameters("The cell to take the value from.")]
151		///
152		#[document_returns("`Some(value)` if the cell still contains a value, `None` otherwise.")]
153		///
154		#[document_examples]
155		///
156		/// ```
157		/// use fp_library::{
158		/// 	brands::*,
159		/// 	classes::*,
160		/// };
161		///
162		/// let cell = ArcBrand::take_cell_new(42);
163		/// assert_eq!(ArcBrand::take_cell_take(&cell), Some(42));
164		/// assert_eq!(ArcBrand::take_cell_take(&cell), None);
165		/// ```
166		fn take_cell_take<'a, T: 'a>(cell: &Arc<Mutex<Option<T>>>) -> Option<T> {
167			// SAFETY: lock only fails on poisoning, which cannot occur here
168			#[expect(
169				clippy::unwrap_used,
170				reason = "Lock only fails on poisoning, which cannot occur here"
171			)]
172			cell.lock().unwrap().take()
173		}
174	}
175
176	impl SendRefCountedPointer for ArcBrand {
177		type Of<'a, T: ?Sized + Send + Sync + 'a> = Arc<T>;
178
179		/// Wraps a sized value in an `Arc`.
180		#[document_signature]
181		///
182		#[document_type_parameters("The lifetime of the value.", "The type of the value to wrap.")]
183		///
184		#[document_parameters("The value to wrap.")]
185		///
186		#[document_returns("The value wrapped in an `Arc`.")]
187		///
188		#[document_examples]
189		///
190		/// ```
191		/// use fp_library::{
192		/// 	brands::*,
193		/// 	functions::*,
194		/// };
195		///
196		/// let ptr = send_ref_counted_pointer_new::<ArcBrand, _>(42);
197		/// assert_eq!(*ptr, 42);
198		/// ```
199		fn new<'a, T: Send + Sync + 'a>(value: T) -> Arc<T> {
200			Arc::new(value)
201		}
202	}
203
204	impl ToDynFn for ArcBrand {
205		/// Coerces a sized closure to a `dyn Fn` wrapped in an `Arc`.
206		#[document_signature]
207		///
208		#[document_type_parameters(
209			"The lifetime of the closure.",
210			"The input type of the function.",
211			"The output type of the function."
212		)]
213		///
214		#[document_parameters("The closure to coerce.")]
215		///
216		#[document_returns("The closure wrapped in an `Arc` as a trait object.")]
217		#[document_examples]
218		///
219		/// ```
220		/// use fp_library::{
221		/// 	brands::*,
222		/// 	classes::*,
223		/// };
224		///
225		/// let f = <ArcBrand as ToDynFn>::new(|x: i32| x + 1);
226		/// assert_eq!(f(1), 2);
227		/// ```
228		fn new<'a, A: 'a, B: 'a>(f: impl 'a + Fn(A) -> B) -> Arc<dyn 'a + Fn(A) -> B> {
229			Arc::new(f)
230		}
231
232		/// Coerces a sized by-reference closure to a `dyn Fn(&A) -> B` wrapped in an `Arc`.
233		#[document_signature]
234		///
235		#[document_type_parameters(
236			"The lifetime of the closure.",
237			"The input type (the closure receives `&A`).",
238			"The output type of the function."
239		)]
240		///
241		#[document_parameters("The closure to coerce.")]
242		///
243		#[document_returns("The closure wrapped in an `Arc` as a by-reference trait object.")]
244		#[document_examples]
245		///
246		/// ```
247		/// use fp_library::{
248		/// 	brands::*,
249		/// 	classes::*,
250		/// };
251		///
252		/// let f = <ArcBrand as ToDynFn>::ref_new(|x: &i32| *x + 1);
253		/// assert_eq!(f(&1), 2);
254		/// ```
255		fn ref_new<'a, A: 'a, B: 'a>(f: impl 'a + Fn(&A) -> B) -> Arc<dyn 'a + Fn(&A) -> B> {
256			Arc::new(f)
257		}
258	}
259
260	impl ToDynCloneFn for ArcBrand {
261		/// Coerces a sized closure to a `dyn Fn` wrapped in an `Arc`.
262		#[document_signature]
263		///
264		#[document_type_parameters(
265			"The lifetime of the closure.",
266			"The input type of the function.",
267			"The output type of the function."
268		)]
269		///
270		#[document_parameters("The closure to coerce.")]
271		///
272		#[document_returns("The closure wrapped in an `Arc` as a trait object.")]
273		///
274		#[document_examples]
275		///
276		/// ```
277		/// use fp_library::{
278		/// 	brands::*,
279		/// 	functions::*,
280		/// };
281		///
282		/// let f = to_dyn_clone_fn::<ArcBrand, _, _>(|x: i32| x + 1);
283		/// assert_eq!(f(1), 2);
284		/// ```
285		fn new<'a, A: 'a, B: 'a>(f: impl 'a + Fn(A) -> B) -> Arc<dyn 'a + Fn(A) -> B> {
286			Arc::new(f)
287		}
288
289		/// Coerces a sized by-reference closure to a `dyn Fn(&A) -> B` wrapped in an `Arc`.
290		#[document_signature]
291		///
292		#[document_type_parameters(
293			"The lifetime of the closure.",
294			"The input type (the closure receives `&A`).",
295			"The output type of the function."
296		)]
297		///
298		#[document_parameters("The closure to coerce.")]
299		///
300		#[document_returns("The closure wrapped in an `Arc` as a by-reference trait object.")]
301		///
302		#[document_examples]
303		///
304		/// ```
305		/// use fp_library::{
306		/// 	brands::*,
307		/// 	classes::*,
308		/// };
309		///
310		/// let f = <ArcBrand as ToDynCloneFn>::ref_new(|x: &i32| *x + 1);
311		/// assert_eq!(f(&1), 2);
312		/// ```
313		fn ref_new<'a, A: 'a, B: 'a>(f: impl 'a + Fn(&A) -> B) -> Arc<dyn 'a + Fn(&A) -> B> {
314			Arc::new(f)
315		}
316	}
317
318	impl ToDynSendFn for ArcBrand {
319		/// Coerces a sized Send+Sync closure to a `dyn Fn + Send + Sync` wrapped in an `Arc`.
320		#[document_signature]
321		///
322		#[document_type_parameters(
323			"The lifetime of the closure.",
324			"The input type of the function.",
325			"The output type of the function."
326		)]
327		///
328		#[document_parameters("The closure to coerce.")]
329		///
330		#[document_returns("The closure wrapped in an `Arc` as a thread-safe trait object.")]
331		///
332		#[document_examples]
333		///
334		/// ```
335		/// use fp_library::{
336		/// 	brands::*,
337		/// 	functions::*,
338		/// };
339		///
340		/// let f = to_dyn_send_fn::<ArcBrand, _, _>(|x: i32| x + 1);
341		/// assert_eq!(f(1), 2);
342		/// ```
343		fn new<'a, A: 'a, B: 'a>(
344			f: impl 'a + Fn(A) -> B + Send + Sync
345		) -> Arc<dyn 'a + Fn(A) -> B + Send + Sync> {
346			Arc::new(f)
347		}
348
349		/// Coerces a sized Send+Sync by-reference closure to a `dyn Fn(&A) -> B + Send + Sync` wrapped in an `Arc`.
350		#[document_signature]
351		///
352		#[document_type_parameters(
353			"The lifetime of the closure.",
354			"The input type (the closure receives `&A`).",
355			"The output type of the function."
356		)]
357		///
358		#[document_parameters("The closure to coerce.")]
359		///
360		#[document_returns(
361			"The closure wrapped in an `Arc` as a thread-safe by-reference trait object."
362		)]
363		///
364		#[document_examples]
365		///
366		/// ```
367		/// use fp_library::{
368		/// 	brands::*,
369		/// 	classes::*,
370		/// };
371		///
372		/// let f = <ArcBrand as ToDynSendFn>::ref_new(|x: &i32| *x + 1);
373		/// assert_eq!(f(&1), 2);
374		/// ```
375		fn ref_new<'a, A: 'a, B: 'a>(
376			f: impl 'a + Fn(&A) -> B + Send + Sync
377		) -> Arc<dyn 'a + Fn(&A) -> B + Send + Sync> {
378			Arc::new(f)
379		}
380	}
381}
382
383#[cfg(test)]
384mod tests {
385	use crate::{
386		brands::ArcBrand,
387		classes::{
388			RefCountedPointer,
389			pointer::new as pointer_new,
390			ref_counted_pointer::new as ref_counted_pointer_new,
391			send_ref_counted_pointer::new as send_ref_counted_pointer_new,
392		},
393	};
394
395	/// Tests that `Pointer::new` correctly creates an `Arc` wrapping the value.
396	#[test]
397	fn test_arc_pointer_new() {
398		let ptr = pointer_new::<ArcBrand, _>(42);
399		assert_eq!(*ptr, 42);
400	}
401
402	/// Tests that `RefCountedPointer::new` correctly creates an `Arc` wrapping the value.
403	#[test]
404	fn test_arc_ref_counted_new() {
405		let ptr = ref_counted_pointer_new::<ArcBrand, _>(42);
406		assert_eq!(*ptr, 42);
407	}
408
409	/// Tests that `SendRefCountedPointer::new` correctly creates an `Arc` wrapping the value.
410	#[test]
411	fn test_arc_send_ref_counted_new() {
412		let ptr = send_ref_counted_pointer_new::<ArcBrand, _>(42);
413		assert_eq!(*ptr, 42);
414	}
415
416	/// Tests that cloning the pointer works as expected (shared ownership).
417	#[test]
418	fn test_arc_clone() {
419		let ptr = ref_counted_pointer_new::<ArcBrand, _>(42);
420		let clone = ptr.clone();
421		assert_eq!(*clone, 42);
422	}
423
424	/// Tests `try_unwrap` behavior:
425	/// - Returns `Ok(value)` when there is only one reference.
426	/// - Returns `Err(ptr)` when there are multiple references.
427	#[test]
428	fn test_arc_try_unwrap() {
429		let ptr = ref_counted_pointer_new::<ArcBrand, _>(42);
430		assert_eq!(ArcBrand::try_unwrap(ptr), Ok(42));
431
432		let ptr = ref_counted_pointer_new::<ArcBrand, _>(42);
433		let _clone = ptr.clone();
434		assert!(ArcBrand::try_unwrap(ptr).is_err());
435	}
436}