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.
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				UnsizedCoercible,
26			},
27		},
28		fp_macros::*,
29		std::{
30			cell::RefCell,
31			rc::Rc,
32		},
33	};
34
35	impl Pointer for RcBrand {
36		type Of<'a, T: ?Sized + 'a> = Rc<T>;
37
38		/// Wraps a sized value in an `Rc`.
39		#[document_signature]
40		///
41		#[document_type_parameters("The lifetime of the value.", "The type of the value to wrap.")]
42		///
43		#[document_parameters("The value to wrap.")]
44		///
45		#[document_returns("The value wrapped in an `Rc`.")]
46		///
47		#[document_examples]
48		///
49		/// ```
50		/// use fp_library::{
51		/// 	brands::*,
52		/// 	functions::*,
53		/// };
54		///
55		/// let ptr = pointer_new::<RcBrand, _>(42);
56		/// assert_eq!(*ptr, 42);
57		/// ```
58		fn new<'a, T: 'a>(value: T) -> Rc<T> {
59			Rc::new(value)
60		}
61	}
62
63	impl RefCountedPointer for RcBrand {
64		type CloneableOf<'a, T: ?Sized + 'a> = Rc<T>;
65		type TakeCellOf<'a, T: 'a> = Rc<RefCell<Option<T>>>;
66
67		/// Wraps a sized value in an `Rc`.
68		#[document_signature]
69		///
70		#[document_type_parameters("The lifetime of the value.", "The type of the value to wrap.")]
71		///
72		#[document_parameters("The value to wrap.")]
73		///
74		#[document_returns("The value wrapped in an `Rc`.")]
75		///
76		#[document_examples]
77		///
78		/// ```
79		/// use fp_library::{
80		/// 	brands::*,
81		/// 	functions::*,
82		/// };
83		///
84		/// let ptr = ref_counted_pointer_new::<RcBrand, _>(42);
85		/// assert_eq!(*ptr, 42);
86		/// ```
87		fn cloneable_new<'a, T: 'a>(value: T) -> Rc<T> {
88			Rc::new(value)
89		}
90
91		/// Attempts to unwrap the inner value if this is the sole reference.
92		#[document_signature]
93		///
94		#[document_type_parameters(
95			"The lifetime of the wrapped value.",
96			"The type of the wrapped value."
97		)]
98		///
99		#[document_parameters("The pointer to attempt to unwrap.")]
100		///
101		#[document_returns("`Ok(value)` if this is the sole reference, otherwise `Err(ptr)`.")]
102		///
103		#[document_examples]
104		///
105		/// ```
106		/// use fp_library::{
107		/// 	brands::*,
108		/// 	functions::*,
109		/// };
110		///
111		/// let ptr = ref_counted_pointer_new::<RcBrand, _>(42);
112		/// assert_eq!(try_unwrap::<RcBrand, _>(ptr), Ok(42));
113		/// ```
114		fn try_unwrap<'a, T: 'a>(ptr: Rc<T>) -> Result<T, Rc<T>> {
115			Rc::try_unwrap(ptr)
116		}
117
118		/// Creates a new take-cell containing the given value.
119		#[document_signature]
120		///
121		#[document_type_parameters("The lifetime of the value.", "The type of the value to store.")]
122		///
123		#[document_parameters("The value to store in the cell.")]
124		///
125		#[document_returns("A new `Rc<RefCell<Option<T>>>` containing the value.")]
126		///
127		#[document_examples]
128		///
129		/// ```
130		/// use fp_library::{
131		/// 	brands::*,
132		/// 	classes::*,
133		/// };
134		///
135		/// let cell = RcBrand::take_cell_new(42);
136		/// assert_eq!(RcBrand::take_cell_take(&cell), Some(42));
137		/// ```
138		fn take_cell_new<'a, T: 'a>(value: T) -> Rc<RefCell<Option<T>>> {
139			Rc::new(RefCell::new(Some(value)))
140		}
141
142		/// Takes the value out of the cell, leaving `None` behind.
143		#[document_signature]
144		///
145		#[document_type_parameters("The lifetime of the value.", "The type of the stored value.")]
146		///
147		#[document_parameters("The cell to take the value from.")]
148		///
149		#[document_returns("`Some(value)` if the cell still contains a value, `None` otherwise.")]
150		///
151		#[document_examples]
152		///
153		/// ```
154		/// use fp_library::{
155		/// 	brands::*,
156		/// 	classes::*,
157		/// };
158		///
159		/// let cell = RcBrand::take_cell_new(42);
160		/// assert_eq!(RcBrand::take_cell_take(&cell), Some(42));
161		/// assert_eq!(RcBrand::take_cell_take(&cell), None);
162		/// ```
163		fn take_cell_take<'a, T: 'a>(cell: &Rc<RefCell<Option<T>>>) -> Option<T> {
164			cell.borrow_mut().take()
165		}
166	}
167
168	impl UnsizedCoercible for RcBrand {
169		/// Coerces a sized closure to a `dyn Fn` wrapped in an `Rc`.
170		#[document_signature]
171		///
172		#[document_type_parameters(
173			"The lifetime of the closure.",
174			"The input type of the function.",
175			"The output type of the function."
176		)]
177		///
178		#[document_parameters("The closure to coerce.")]
179		///
180		#[document_returns("The closure wrapped in an `Rc` as a trait object.")]
181		///
182		#[document_examples]
183		///
184		/// ```
185		/// use fp_library::{
186		/// 	brands::*,
187		/// 	functions::*,
188		/// };
189		///
190		/// let f = coerce_fn::<RcBrand, _, _>(|x: i32| x + 1);
191		/// assert_eq!(f(1), 2);
192		/// ```
193		fn coerce_fn<'a, A: 'a, B: 'a>(f: impl 'a + Fn(A) -> B) -> Rc<dyn 'a + Fn(A) -> B> {
194			Rc::new(f)
195		}
196	}
197}
198
199#[cfg(test)]
200mod tests {
201
202	use crate::{
203		brands::RcBrand,
204		classes::{
205			RefCountedPointer,
206			pointer::new,
207			ref_counted_pointer::cloneable_new,
208		},
209	};
210
211	/// Tests that `pointer_new` correctly creates an `Rc` wrapping the value.
212	#[test]
213	fn test_rc_new() {
214		let ptr = new::<RcBrand, _>(42);
215		assert_eq!(*ptr, 42);
216	}
217
218	/// Tests that `ref_counted_pointer_new` correctly creates an `Rc` wrapping the value.
219	#[test]
220	fn test_rc_cloneable_new() {
221		let ptr = cloneable_new::<RcBrand, _>(42);
222		assert_eq!(*ptr, 42);
223	}
224
225	/// Tests that cloning the pointer works as expected (shared ownership).
226	#[test]
227	fn test_rc_clone() {
228		let ptr = cloneable_new::<RcBrand, _>(42);
229		let clone = ptr.clone();
230		assert_eq!(*clone, 42);
231	}
232
233	/// Tests `try_unwrap` behavior:
234	/// - Returns `Ok(value)` when there is only one reference.
235	/// - Returns `Err(ptr)` when there are multiple references.
236	#[test]
237	fn test_rc_try_unwrap() {
238		let ptr = cloneable_new::<RcBrand, _>(42);
239		assert_eq!(RcBrand::try_unwrap(ptr), Ok(42));
240
241		let ptr = cloneable_new::<RcBrand, _>(42);
242		let _clone = ptr.clone();
243		assert!(RcBrand::try_unwrap(ptr).is_err());
244	}
245}