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			#[expect(
168				clippy::unwrap_used,
169				reason = "Lock only fails on poisoning, which cannot occur here"
170			)]
171			cell.lock().unwrap().take()
172		}
173	}
174
175	impl SendRefCountedPointer for ArcBrand {
176		type SendOf<'a, T: ?Sized + Send + Sync + 'a> = Arc<T>;
177
178		/// Wraps a sized value in an `Arc`.
179		#[document_signature]
180		///
181		#[document_type_parameters("The lifetime of the value.", "The type of the value to wrap.")]
182		///
183		#[document_parameters("The value to wrap.")]
184		///
185		#[document_returns("The value wrapped in an `Arc`.")]
186		///
187		#[document_examples]
188		///
189		/// ```
190		/// use fp_library::{
191		/// 	brands::*,
192		/// 	functions::*,
193		/// };
194		///
195		/// let ptr = send_ref_counted_pointer_new::<ArcBrand, _>(42);
196		/// assert_eq!(*ptr, 42);
197		/// ```
198		fn send_new<'a, T: Send + Sync + 'a>(value: T) -> Arc<T> {
199			Arc::new(value)
200		}
201	}
202
203	impl UnsizedCoercible for ArcBrand {
204		/// Coerces a sized closure to a `dyn Fn` wrapped in an `Arc`.
205		#[document_signature]
206		///
207		#[document_type_parameters(
208			"The lifetime of the closure.",
209			"The input type of the function.",
210			"The output type of the function."
211		)]
212		///
213		#[document_parameters("The closure to coerce.")]
214		///
215		#[document_returns("The closure wrapped in an `Arc` as a trait object.")]
216		///
217		#[document_examples]
218		///
219		/// ```
220		/// use fp_library::{
221		/// 	brands::*,
222		/// 	functions::*,
223		/// };
224		///
225		/// let f = coerce_fn::<ArcBrand, _, _>(|x: i32| x + 1);
226		/// assert_eq!(f(1), 2);
227		/// ```
228		fn coerce_fn<'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		///
245		#[document_examples]
246		///
247		/// ```
248		/// use fp_library::{
249		/// 	brands::*,
250		/// 	classes::*,
251		/// };
252		///
253		/// let f = ArcBrand::coerce_ref_fn(|x: &i32| *x + 1);
254		/// assert_eq!(f(&1), 2);
255		/// ```
256		fn coerce_ref_fn<'a, A: 'a, B: 'a>(f: impl 'a + Fn(&A) -> B) -> Arc<dyn 'a + Fn(&A) -> B> {
257			Arc::new(f)
258		}
259	}
260
261	impl SendUnsizedCoercible for ArcBrand {
262		/// Coerces a sized Send+Sync closure to a `dyn Fn + Send + Sync` wrapped in an `Arc`.
263		#[document_signature]
264		///
265		#[document_type_parameters(
266			"The lifetime of the closure.",
267			"The input type of the function.",
268			"The output type of the function."
269		)]
270		///
271		#[document_parameters("The closure to coerce.")]
272		///
273		#[document_returns("The closure wrapped in an `Arc` as a thread-safe trait object.")]
274		///
275		#[document_examples]
276		///
277		/// ```
278		/// use fp_library::{
279		/// 	brands::*,
280		/// 	functions::*,
281		/// };
282		///
283		/// let f = coerce_send_fn::<ArcBrand, _, _>(|x: i32| x + 1);
284		/// assert_eq!(f(1), 2);
285		/// ```
286		fn coerce_send_fn<'a, A: 'a, B: 'a>(
287			f: impl 'a + Fn(A) -> B + Send + Sync
288		) -> Arc<dyn 'a + Fn(A) -> B + Send + Sync> {
289			Arc::new(f)
290		}
291
292		/// Coerces a sized Send+Sync by-reference closure to a `dyn Fn(&A) -> B + Send + Sync` wrapped in an `Arc`.
293		#[document_signature]
294		///
295		#[document_type_parameters(
296			"The lifetime of the closure.",
297			"The input type (the closure receives `&A`).",
298			"The output type of the function."
299		)]
300		///
301		#[document_parameters("The closure to coerce.")]
302		///
303		#[document_returns(
304			"The closure wrapped in an `Arc` as a thread-safe by-reference trait object."
305		)]
306		///
307		#[document_examples]
308		///
309		/// ```
310		/// use fp_library::{
311		/// 	brands::*,
312		/// 	classes::*,
313		/// };
314		///
315		/// let f = ArcBrand::coerce_send_ref_fn(|x: &i32| *x + 1);
316		/// assert_eq!(f(&1), 2);
317		/// ```
318		fn coerce_send_ref_fn<'a, A: 'a, B: 'a>(
319			f: impl 'a + Fn(&A) -> B + Send + Sync
320		) -> Arc<dyn 'a + Fn(&A) -> B + Send + Sync> {
321			Arc::new(f)
322		}
323	}
324}
325
326#[cfg(test)]
327mod tests {
328	use crate::{
329		brands::ArcBrand,
330		classes::{
331			RefCountedPointer,
332			pointer::new,
333			ref_counted_pointer::cloneable_new,
334			send_ref_counted_pointer::send_new,
335		},
336	};
337
338	/// Tests that `pointer_new` correctly creates an `Arc` wrapping the value.
339	#[test]
340	fn test_arc_new() {
341		let ptr = new::<ArcBrand, _>(42);
342		assert_eq!(*ptr, 42);
343	}
344
345	/// Tests that `ref_counted_pointer_new` correctly creates an `Arc` wrapping the value.
346	#[test]
347	fn test_arc_cloneable_new() {
348		let ptr = cloneable_new::<ArcBrand, _>(42);
349		assert_eq!(*ptr, 42);
350	}
351
352	/// Tests that `send_ref_counted_pointer_new` correctly creates an `Arc` wrapping the value.
353	#[test]
354	fn test_arc_send_new() {
355		let ptr = send_new::<ArcBrand, _>(42);
356		assert_eq!(*ptr, 42);
357	}
358
359	/// Tests that cloning the pointer works as expected (shared ownership).
360	#[test]
361	fn test_arc_clone() {
362		let ptr = cloneable_new::<ArcBrand, _>(42);
363		let clone = ptr.clone();
364		assert_eq!(*clone, 42);
365	}
366
367	/// Tests `try_unwrap` behavior:
368	/// - Returns `Ok(value)` when there is only one reference.
369	/// - Returns `Err(ptr)` when there are multiple references.
370	#[test]
371	fn test_arc_try_unwrap() {
372		let ptr = cloneable_new::<ArcBrand, _>(42);
373		assert_eq!(ArcBrand::try_unwrap(ptr), Ok(42));
374
375		let ptr = cloneable_new::<ArcBrand, _>(42);
376		let _clone = ptr.clone();
377		assert!(ArcBrand::try_unwrap(ptr).is_err());
378	}
379}