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