Skip to main content

fp_library/types/
rc_ptr.rs

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