Skip to main content

fp_library/classes/
send_clone_fn.rs

1//! Thread-safe cloneable wrappers over closures that carry `Send + Sync` bounds.
2//!
3//! ### Examples
4//!
5//! ```
6//! use {
7//! 	fp_library::{
8//! 		brands::*,
9//! 		functions::*,
10//! 	},
11//! 	std::thread,
12//! };
13//!
14//! let f = send_lift_fn_new::<ArcFnBrand, _, _>(|x: i32| x * 2);
15//!
16//! // Can be sent to another thread
17//! let handle = thread::spawn(move || {
18//! 	assert_eq!(f(5), 10);
19//! });
20//! handle.join().unwrap();
21//! ```
22
23#[fp_macros::document_module]
24mod inner {
25	use {
26		crate::dispatch::{
27			ClosureMode,
28			Ref,
29			Val,
30		},
31		fp_macros::*,
32		std::ops::Deref,
33	};
34
35	/// Abstraction for thread-safe cloneable wrappers over closures.
36	///
37	/// This trait mirrors [`CloneFn`] with additional `Send + Sync` bounds on the
38	/// wrapped closure and the wrapper itself. It is a separate, independent trait
39	/// (not a supertrait of `CloneFn`) because its [`Of`](Self::Of) associated type
40	/// derefs to `dyn Fn + Send + Sync`, which is a different unsized type than the
41	/// `dyn Fn` target used by `CloneFn::Of`. Types like
42	/// [`FnBrand<P>`](crate::brands::FnBrand) implement both traits when the
43	/// pointer `P` supports it ([`ArcFnBrand`][crate::brands::ArcFnBrand]
44	/// implements both; [`RcFnBrand`][crate::brands::RcFnBrand] implements only
45	/// `CloneFn`).
46	///
47	/// The `Mode` parameter selects whether the wrapped closure takes its input
48	/// by value (`Val`, the default) or by reference (`Ref`).
49	///
50	/// The lifetime `'a` ensures the function doesn't outlive referenced data,
51	/// while generic types `A` and `B` represent the input and output types, respectively.
52	///
53	/// By explicitly requiring that both type parameters outlive the application lifetime `'a`,
54	/// we provide the compiler with the necessary guarantees to handle trait objects
55	/// (like `dyn Fn`) commonly used in thread-safe function wrappers. This resolves potential
56	/// E0310 errors where the compiler cannot otherwise prove that captured variables in
57	/// closures satisfy the required lifetime bounds.
58	#[document_type_parameters(
59		"Selects whether the wrapped closure takes its input by value (`Val`) or by reference (`Ref`). Defaults to `Val`."
60	)]
61	pub trait SendCloneFn<Mode: ClosureMode = Val> {
62		/// The type of the thread-safe cloneable function wrapper.
63		///
64		/// This associated type represents the concrete type of the wrapper (e.g., `Arc<dyn Fn(A) -> B + Send + Sync>`)
65		/// that implements `Clone`, `Send`, `Sync` and dereferences to the underlying closure.
66		type Of<'a, A: 'a, B: 'a>: 'a
67			+ Clone
68			+ Send
69			+ Sync
70			+ Deref<Target = Mode::SendTarget<'a, A, B>>;
71	}
72
73	/// A trait for constructing thread-safe cloneable function wrappers from closures.
74	///
75	/// Separated from [`SendCloneFn`] because the `new` method's parameter type
76	/// depends on the closure mode (`Fn(A) -> B + Send + Sync` for `Val`), and a single
77	/// trait method cannot have a mode-dependent signature.
78	pub trait SendLiftFn: SendCloneFn<Val> {
79		/// Creates a new thread-safe cloneable function wrapper.
80		///
81		/// This method wraps a closure into a thread-safe cloneable function wrapper.
82		#[document_signature]
83		///
84		#[document_type_parameters(
85			"The lifetime of the function and its captured data.",
86			"The input type of the function.",
87			"The output type of the function."
88		)]
89		///
90		#[document_parameters("The closure to wrap. Must be `Send + Sync`.")]
91		#[document_returns("The wrapped thread-safe cloneable function.")]
92		#[document_examples]
93		///
94		/// ```
95		/// use {
96		/// 	fp_library::{
97		/// 		brands::*,
98		/// 		functions::*,
99		/// 	},
100		/// 	std::thread,
101		/// };
102		///
103		/// let f = send_lift_fn_new::<ArcFnBrand, _, _>(|x: i32| x * 2);
104		///
105		/// // Can be sent to another thread
106		/// let handle = thread::spawn(move || {
107		/// 	assert_eq!(f(5), 10);
108		/// });
109		/// handle.join().unwrap();
110		/// ```
111		fn new<'a, A: 'a, B: 'a>(
112			f: impl 'a + Fn(A) -> B + Send + Sync
113		) -> <Self as SendCloneFn>::Of<'a, A, B>;
114	}
115
116	/// Creates a new thread-safe cloneable function wrapper.
117	///
118	/// Free function version that dispatches to [the type class' associated function][`SendLiftFn::new`].
119	#[document_signature]
120	///
121	#[document_type_parameters(
122		"The lifetime of the function and its captured data.",
123		"The brand of the thread-safe cloneable function wrapper.",
124		"The input type of the function.",
125		"The output type of the function."
126	)]
127	///
128	#[document_parameters("The closure to wrap. Must be `Send + Sync`.")]
129	#[document_returns("The wrapped thread-safe cloneable function.")]
130	#[document_examples]
131	///
132	/// ```
133	/// use {
134	/// 	fp_library::{
135	/// 		brands::*,
136	/// 		functions::*,
137	/// 	},
138	/// 	std::thread,
139	/// };
140	///
141	/// let f = send_lift_fn_new::<ArcFnBrand, _, _>(|x: i32| x * 2);
142	///
143	/// // Can be sent to another thread
144	/// let handle = thread::spawn(move || {
145	/// 	assert_eq!(f(5), 10);
146	/// });
147	/// handle.join().unwrap();
148	/// ```
149	pub fn new<'a, Brand, A, B>(
150		f: impl 'a + Fn(A) -> B + Send + Sync
151	) -> <Brand as SendCloneFn>::Of<'a, A, B>
152	where
153		Brand: SendLiftFn, {
154		<Brand as SendLiftFn>::new(f)
155	}
156
157	/// A trait for constructing thread-safe Ref-mode cloneable function wrappers.
158	///
159	/// This mirrors [`SendLiftFn`] but for by-reference closures (`Fn(&A) -> B + Send + Sync`).
160	pub trait SendRefLiftFn: SendCloneFn<Ref> {
161		/// Creates a new thread-safe cloneable by-reference function wrapper.
162		#[document_signature]
163		///
164		#[document_type_parameters(
165			"The lifetime of the function and its captured data.",
166			"The input type (the closure receives `&A`).",
167			"The output type of the function."
168		)]
169		///
170		#[document_parameters("The by-reference closure to wrap. Must be `Send + Sync`.")]
171		#[document_returns("The wrapped thread-safe cloneable by-reference function.")]
172		#[document_examples]
173		///
174		/// ```
175		/// use fp_library::{
176		/// 	brands::*,
177		/// 	functions::*,
178		/// };
179		///
180		/// let f = send_ref_lift_fn_new::<ArcFnBrand, _, _>(|x: &i32| *x * 2);
181		/// assert_eq!(f(&5), 10);
182		/// ```
183		fn ref_new<'a, A: 'a, B: 'a>(
184			f: impl 'a + Fn(&A) -> B + Send + Sync
185		) -> <Self as SendCloneFn<Ref>>::Of<'a, A, B>;
186	}
187
188	/// Creates a new thread-safe cloneable by-reference function wrapper.
189	///
190	/// Free function version that dispatches to [`SendRefLiftFn::ref_new`].
191	#[document_signature]
192	///
193	#[document_type_parameters(
194		"The lifetime of the function and its captured data.",
195		"The brand of the thread-safe cloneable function wrapper.",
196		"The input type (the closure receives `&A`).",
197		"The output type of the function."
198	)]
199	///
200	#[document_parameters("The by-reference closure to wrap. Must be `Send + Sync`.")]
201	#[document_returns("The wrapped thread-safe cloneable by-reference function.")]
202	#[document_examples]
203	///
204	/// ```
205	/// use fp_library::{
206	/// 	brands::*,
207	/// 	functions::*,
208	/// };
209	///
210	/// let f = send_ref_lift_fn_new::<ArcFnBrand, _, _>(|x: &i32| *x * 2);
211	/// assert_eq!(f(&5), 10);
212	/// ```
213	pub fn ref_new<'a, Brand, A, B>(
214		f: impl 'a + Fn(&A) -> B + Send + Sync
215	) -> <Brand as SendCloneFn<Ref>>::Of<'a, A, B>
216	where
217		Brand: SendRefLiftFn, {
218		<Brand as SendRefLiftFn>::ref_new(f)
219	}
220}
221
222pub use inner::*;