qubit_function/consumers/bi_consumer_once.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026.
4 * Haixing Hu, Qubit Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9//! # BiConsumerOnce Types
10//!
11//! Provides one-time bi-consumer interface implementations for operations
12//! accepting two input parameters without returning a result.
13//!
14//! It is similar to the `FnOnce(&T, &U)` trait in the standard library.
15//!
16//! This module provides a unified `BiConsumerOnce` trait and one concrete
17//! implementation:
18//!
19//! - **`BoxBiConsumerOnce<T, U>`**: Box-based single ownership
20//! implementation
21//!
22//! # Why No Arc/Rc Variants?
23//!
24//! Unlike reusable [`BiConsumer`](crate::consumers::BiConsumer)
25//! implementations, this module does **not** provide `ArcBiConsumerOnce` or
26//! `RcBiConsumerOnce` implementations. This is a design decision based on the
27//! fundamental incompatibility between `FnOnce` semantics and shared ownership.
28//! See the design documentation for details.
29//!
30//! # Design Philosophy
31//!
32//! BiConsumerOnce uses `FnOnce(&T, &U)` semantics: for truly one-time
33//! consumption operations.
34//!
35//! Unlike BiConsumer, BiConsumerOnce consumes itself on first call. Suitable
36//! for initialization callbacks, cleanup callbacks, etc.
37//!
38//! # Author
39//!
40//! Haixing Hu
41use crate::{
42 consumers::macros::{
43 impl_box_conditional_consumer,
44 impl_box_consumer_methods,
45 impl_conditional_consumer_debug_display,
46 impl_consumer_common_methods,
47 impl_consumer_debug_display,
48 },
49 macros::{
50 impl_box_once_conversions,
51 impl_closure_once_trait,
52 },
53 predicates::bi_predicate::{
54 BiPredicate,
55 BoxBiPredicate,
56 },
57};
58
59// ==========================================================================
60// Type Aliases
61// ==========================================================================
62
63/// Type alias for bi-consumer once function signature.
64type BiConsumerOnceFn<T, U> = dyn FnOnce(&T, &U);
65
66mod box_bi_consumer_once;
67pub use box_bi_consumer_once::BoxBiConsumerOnce;
68mod fn_bi_consumer_once_ops;
69pub use fn_bi_consumer_once_ops::FnBiConsumerOnceOps;
70mod box_conditional_bi_consumer_once;
71pub use box_conditional_bi_consumer_once::BoxConditionalBiConsumerOnce;
72
73// =======================================================================
74// 1. BiConsumerOnce Trait - Unified Interface
75// =======================================================================
76
77/// BiConsumerOnce trait - Unified one-time bi-consumer interface
78///
79/// It is similar to the `FnOnce(&T, &U)` trait in the standard library.
80///
81/// Defines core behavior for all one-time bi-consumer types. Similar to a
82/// bi-consumer implementing `FnOnce(&T, &U)`, performs operations
83/// accepting two value references but returning no result (side effects
84/// only), consuming itself in the process.
85///
86/// # Automatic Implementations
87///
88/// - All closures implementing `FnOnce(&T, &U)`
89/// - `BoxBiConsumerOnce<T, U>`
90///
91/// # Features
92///
93/// - **Unified Interface**: All bi-consumer types share the same `accept`
94/// method signature
95/// - **Automatic Implementation**: Closures automatically implement this
96/// trait with zero overhead
97/// - **Type Conversions**: Can convert to BoxBiConsumerOnce
98/// - **Generic Programming**: Write functions accepting any one-time
99/// bi-consumer type
100///
101/// # Examples
102///
103/// ```rust
104/// use qubit_function::{BiConsumerOnce, BoxBiConsumerOnce};
105/// use std::sync::{Arc, Mutex};
106///
107/// fn apply_consumer<C: BiConsumerOnce<i32, i32>>(
108/// consumer: C,
109/// a: &i32,
110/// b: &i32
111/// ) {
112/// consumer.accept(a, b);
113/// }
114///
115/// let log = Arc::new(Mutex::new(Vec::new()));
116/// let l = log.clone();
117/// let box_con = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
118/// l.lock().unwrap().push(*x + *y);
119/// });
120/// apply_consumer(box_con, &5, &3);
121/// assert_eq!(*log.lock().unwrap(), vec![8]);
122/// ```
123///
124/// # Author
125///
126/// Haixing Hu
127pub trait BiConsumerOnce<T, U> {
128 /// Performs the one-time consumption operation
129 ///
130 /// Executes an operation on the given two references. The operation
131 /// typically reads input values or produces side effects, but does not
132 /// modify the input values themselves. Consumes self.
133 ///
134 /// # Parameters
135 ///
136 /// * `first` - Reference to the first value to consume
137 /// * `second` - Reference to the second value to consume
138 ///
139 /// # Examples
140 ///
141 /// ```rust
142 /// use qubit_function::{BiConsumerOnce, BoxBiConsumerOnce};
143 ///
144 /// let consumer = BoxBiConsumerOnce::new(|x: &i32, y: &i32| {
145 /// println!("Sum: {}", x + y);
146 /// });
147 /// consumer.accept(&5, &3);
148 /// ```
149 fn accept(self, first: &T, second: &U);
150
151 /// Converts to BoxBiConsumerOnce
152 ///
153 /// **⚠️ Consumes `self`**: Original consumer becomes unavailable after
154 /// calling this method.
155 ///
156 /// # Returns
157 ///
158 /// Returns the wrapped `BoxBiConsumerOnce<T, U>`
159 ///
160 /// # Examples
161 ///
162 /// ```rust
163 /// use qubit_function::BiConsumerOnce;
164 /// use std::sync::{Arc, Mutex};
165 ///
166 /// let log = Arc::new(Mutex::new(Vec::new()));
167 /// let l = log.clone();
168 /// let closure = move |x: &i32, y: &i32| {
169 /// l.lock().unwrap().push(*x + *y);
170 /// };
171 /// let box_consumer = closure.into_box();
172 /// box_consumer.accept(&5, &3);
173 /// assert_eq!(*log.lock().unwrap(), vec![8]);
174 /// ```
175 fn into_box(self) -> BoxBiConsumerOnce<T, U>
176 where
177 Self: Sized + 'static,
178 {
179 BoxBiConsumerOnce::new(move |t, u| self.accept(t, u))
180 }
181
182 /// Converts to a closure
183 ///
184 /// **⚠️ Consumes `self`**: Original consumer becomes unavailable after
185 /// calling this method.
186 ///
187 /// Converts the one-time bi-consumer to a closure usable with standard
188 /// library methods requiring `FnOnce`.
189 ///
190 /// # Returns
191 ///
192 /// Returns a closure implementing `FnOnce(&T, &U)`
193 fn into_fn(self) -> impl FnOnce(&T, &U)
194 where
195 Self: Sized + 'static,
196 {
197 move |t, u| self.accept(t, u)
198 }
199
200 /// Convert to BoxBiConsumerOnce without consuming self
201 ///
202 /// **⚠️ Requires Clone**: This method requires `Self` to implement
203 /// `Clone`. Clones the current bi-consumer and then converts the clone
204 /// to a `BoxBiConsumerOnce`.
205 ///
206 /// # Returns
207 ///
208 /// Returns the wrapped `BoxBiConsumerOnce<T, U>`
209 ///
210 /// # Examples
211 ///
212 /// ```rust
213 /// use qubit_function::BiConsumerOnce;
214 /// use std::sync::{Arc, Mutex};
215 ///
216 /// let log = Arc::new(Mutex::new(Vec::new()));
217 /// let l = log.clone();
218 /// let closure = move |x: &i32, y: &i32| {
219 /// l.lock().unwrap().push(*x + *y);
220 /// };
221 /// let box_consumer = closure.to_box();
222 /// box_consumer.accept(&5, &3);
223 /// assert_eq!(*log.lock().unwrap(), vec![8]);
224 /// ```
225 fn to_box(&self) -> BoxBiConsumerOnce<T, U>
226 where
227 Self: Sized + Clone + 'static,
228 {
229 self.clone().into_box()
230 }
231
232 /// Convert to closure without consuming self
233 ///
234 /// **⚠️ Requires Clone**: This method requires `Self` to implement
235 /// `Clone`. Clones the current bi-consumer and then converts the clone
236 /// to a closure.
237 ///
238 /// # Returns
239 ///
240 /// Returns a closure implementing `FnOnce(&T, &U)`
241 ///
242 /// # Examples
243 ///
244 /// ```rust
245 /// use qubit_function::BiConsumerOnce;
246 /// use std::sync::{Arc, Mutex};
247 ///
248 /// let log = Arc::new(Mutex::new(Vec::new()));
249 /// let l = log.clone();
250 /// let closure = move |x: &i32, y: &i32| {
251 /// l.lock().unwrap().push(*x + *y);
252 /// };
253 /// let func = closure.to_fn();
254 /// func(&5, &3);
255 /// assert_eq!(*log.lock().unwrap(), vec![8]);
256 /// ```
257 fn to_fn(&self) -> impl FnOnce(&T, &U)
258 where
259 Self: Sized + Clone + 'static,
260 {
261 self.clone().into_fn()
262 }
263}