Skip to main content

qubit_function/functions/
bi_mutating_function_once.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9
10//! # BiMutatingFunctionOnce Types
11//!
12//! Provides Rust implementations of consuming bi-mutating-function traits similar to
13//! Rust's `FnOnce(&mut T, &mut U) -> R` trait, but with value-oriented semantics for functional
14//! programming patterns with two mutable input references.
15//!
16//! This module provides the `BiMutatingFunctionOnce<T, U, R>` trait and one-time use
17//! implementations:
18//!
19//! - [`BoxBiMutatingFunctionOnce`]: Single ownership, one-time use
20//!
21//! # Author
22//!
23//! Haixing Hu
24use crate::functions::{
25    macros::{
26        impl_box_conditional_function,
27        impl_box_function_methods,
28        impl_conditional_function_debug_display,
29        impl_function_common_methods,
30        impl_function_constant_method,
31        impl_function_debug_display,
32    },
33    mutating_function_once::MutatingFunctionOnce,
34};
35use crate::macros::{
36    impl_box_once_conversions,
37    impl_closure_once_trait,
38};
39use crate::predicates::bi_predicate::{
40    BiPredicate,
41    BoxBiPredicate,
42};
43
44// ============================================================================
45// Core Trait
46// ============================================================================
47
48/// BiMutatingFunctionOnce trait - consuming bi-mutating-function that takes
49/// mutable references
50///
51/// Defines the behavior of a consuming bi-mutating-function: computing a value of
52/// type `R` from mutable references to types `T` and `U` by taking ownership of self.
53/// This trait is analogous to `FnOnce(&mut T, &mut U) -> R`.
54///
55/// # Type Parameters
56///
57/// * `T` - The type of the first input value (mutable reference)
58/// * `U` - The type of the second input value (mutable reference)
59/// * `R` - The type of the output value
60///
61/// # Author
62///
63/// Haixing Hu
64pub trait BiMutatingFunctionOnce<T, U, R> {
65    /// Computes output from two mutable references, consuming self
66    ///
67    /// # Parameters
68    ///
69    /// * `first` - Mutable reference to the first input value
70    /// * `second` - Mutable reference to the second input value
71    ///
72    /// # Returns
73    ///
74    /// The computed output value
75    fn apply(self, first: &mut T, second: &mut U) -> R;
76
77    /// Converts to BoxBiMutatingFunctionOnce
78    ///
79    /// **⚠️ Consumes `self`**: The original bi-function becomes unavailable
80    /// after calling this method.
81    ///
82    /// # Returns
83    ///
84    /// Returns `BoxBiMutatingFunctionOnce<T, U, R>`
85    fn into_box(self) -> BoxBiMutatingFunctionOnce<T, U, R>
86    where
87        Self: Sized + 'static,
88        T: 'static,
89        U: 'static,
90        R: 'static,
91    {
92        BoxBiMutatingFunctionOnce::new(move |t: &mut T, u: &mut U| self.apply(t, u))
93    }
94
95    /// Converts bi-mutating-function to a closure
96    ///
97    /// **⚠️ Consumes `self`**: The original bi-function becomes unavailable
98    /// after calling this method.
99    ///
100    /// # Returns
101    ///
102    /// Returns a closure that implements `FnOnce(&mut T, &mut U) -> R`
103    fn into_fn(self) -> impl FnOnce(&mut T, &mut U) -> R
104    where
105        Self: Sized + 'static,
106    {
107        move |t: &mut T, u: &mut U| self.apply(t, u)
108    }
109
110    /// Converts bi-mutating-function to a boxed function pointer
111    ///
112    /// **📌 Borrows `&self`**: The original bi-function remains usable
113    /// after calling this method.
114    ///
115    /// # Returns
116    ///
117    /// Returns a boxed function pointer that implements `FnOnce(&mut T, &mut U) -> R`
118    ///
119    /// # Examples
120    ///
121    /// ```rust
122    /// use qubit_function::BiMutatingFunctionOnce;
123    ///
124    /// let swap_and_sum = |x: &mut i32, y: &mut i32| {
125    ///     let temp = *x;
126    ///     *x = *y;
127    ///     *y = temp;
128    ///     *x + *y
129    /// };
130    /// let func = swap_and_sum.to_box();
131    /// let mut a = 20;
132    /// let mut b = 22;
133    /// assert_eq!(func.apply(&mut a, &mut b), 42);
134    /// ```
135    fn to_box(&self) -> BoxBiMutatingFunctionOnce<T, U, R>
136    where
137        Self: Clone + 'static,
138        T: 'static,
139        U: 'static,
140        R: 'static,
141    {
142        self.clone().into_box()
143    }
144
145    /// Converts bi-mutating-function to a closure
146    ///
147    /// **📌 Borrows `&self`**: The original bi-function remains usable
148    /// after calling this method.
149    ///
150    /// # Returns
151    ///
152    /// Returns a closure that implements `FnOnce(&mut T, &mut U) -> R`
153    ///
154    /// # Examples
155    ///
156    /// ```rust
157    /// use qubit_function::BiMutatingFunctionOnce;
158    ///
159    /// let swap_and_sum = |x: &mut i32, y: &mut i32| {
160    ///     let temp = *x;
161    ///     *x = *y;
162    ///     *y = temp;
163    ///     *x + *y
164    /// };
165    /// let func = swap_and_sum.to_fn();
166    /// let mut a = 20;
167    /// let mut b = 22;
168    /// assert_eq!(func(&mut a, &mut b), 42);
169    /// ```
170    fn to_fn(&self) -> impl FnOnce(&mut T, &mut U) -> R
171    where
172        Self: Clone + 'static,
173    {
174        self.clone().into_fn()
175    }
176}
177
178// ============================================================================
179// BoxBiMutatingFunctionOnce - Box<dyn FnOnce(&mut T, &mut U) -> R>
180// ============================================================================
181
182/// BoxBiMutatingFunctionOnce - consuming bi-mutating-function wrapper based on
183/// `Box<dyn FnOnce>`
184///
185/// A bi-mutating-function wrapper that provides single ownership with one-time use
186/// semantics. Consumes self and borrows both input values mutably.
187///
188/// # Features
189///
190/// - **Based on**: `Box<dyn FnOnce(&mut T, &mut U) -> R>`
191/// - **Ownership**: Single ownership, cannot be cloned
192/// - **Reusability**: Can only be called once (consumes self)
193/// - **Thread Safety**: Not thread-safe (no `Send + Sync` requirement)
194///
195/// # Author
196///
197/// Haixing Hu
198pub struct BoxBiMutatingFunctionOnce<T, U, R> {
199    function: Box<dyn FnOnce(&mut T, &mut U) -> R>,
200    name: Option<String>,
201}
202
203// Implement BoxBiMutatingFunctionOnce
204impl<T, U, R> BoxBiMutatingFunctionOnce<T, U, R> {
205    // Generate new(), new_with_name(), new_with_optional_name(), name(), set_name()
206    impl_function_common_methods!(
207        BoxBiMutatingFunctionOnce<T, U, R>,
208        (FnOnce(&mut T, &mut U) -> R + 'static),
209        |f| Box::new(f)
210    );
211
212    // Generate when(), and_then()
213    impl_box_function_methods!(
214        BoxBiMutatingFunctionOnce<T, U, R>,
215        BoxConditionalBiMutatingFunctionOnce,
216        MutatingFunctionOnce
217    );
218}
219
220// Implement BiMutatingFunctionOnce trait for BoxBiMutatingFunctionOnce
221impl<T, U, R> BiMutatingFunctionOnce<T, U, R> for BoxBiMutatingFunctionOnce<T, U, R> {
222    fn apply(self, first: &mut T, second: &mut U) -> R {
223        (self.function)(first, second)
224    }
225
226    // Generate into_box(), into_fn(), to_box()
227    impl_box_once_conversions!(
228        BoxBiMutatingFunctionOnce<T, U, R>,
229        BiMutatingFunctionOnce,
230        FnOnce(&mut T, &mut U) -> R
231    );
232}
233
234// Implement constant method for BoxBiMutatingFunctionOnce
235impl_function_constant_method!(BoxBiMutatingFunctionOnce<T, U, R>);
236
237// Use macro to generate Debug and Display implementations
238impl_function_debug_display!(BoxBiMutatingFunctionOnce<T, U, R>);
239
240// ============================================================================
241// Blanket implementation for standard FnOnce trait
242// ============================================================================
243
244// Implement BiMutatingFunctionOnce for all FnOnce(&mut T, &mut U) -> R using macro
245impl_closure_once_trait!(
246    BiMutatingFunctionOnce<T, U, R>,
247    apply,
248    BoxBiMutatingFunctionOnce,
249    FnOnce(first: &mut T, second: &mut U) -> R
250);
251
252// ============================================================================
253// FnBiMutatingFunctionOnceOps - Extension trait for FnOnce(&mut T, &mut U) -> R bi-functions
254// ============================================================================
255
256/// Extension trait for closures implementing `FnOnce(&mut T, &mut U) -> R`
257///
258/// Provides composition methods (`and_then`, `when`) for one-time use
259/// bi-mutating-function closures and function pointers without requiring explicit
260/// wrapping in `BoxBiMutatingFunctionOnce`.
261///
262/// This trait is automatically implemented for all closures and function
263/// pointers that implement `FnOnce(&mut T, &mut U) -> R`.
264///
265/// # Design Rationale
266///
267/// While closures automatically implement `BiMutatingFunctionOnce<T, U, R>` through
268/// blanket implementation, they don't have access to instance methods like
269/// `and_then` and `when`. This extension trait provides those methods,
270/// returning `BoxBiMutatingFunctionOnce` for maximum flexibility.
271///
272/// # Examples
273///
274/// ## Chain composition with and_then
275///
276/// ```rust
277/// use qubit_function::{BiMutatingFunctionOnce, FnBiMutatingFunctionOnceOps};
278///
279/// let swap_and_sum = |x: &mut i32, y: &mut i32| {
280///     let temp = *x;
281///     *x = *y;
282///     *y = temp;
283///     *x + *y
284/// };
285/// let double = |x: i32| x * 2;
286///
287/// let composed = swap_and_sum.and_then(double);
288/// let mut a = 3;
289/// let mut b = 5;
290/// assert_eq!(composed.apply(&mut a, &mut b), 16); // (5 + 3) * 2 = 16
291/// ```
292///
293/// ## Conditional execution with when
294///
295/// ```rust
296/// use qubit_function::{BiMutatingFunctionOnce, FnBiMutatingFunctionOnceOps};
297///
298/// let swap_and_sum = |x: &mut i32, y: &mut i32| {
299///     let temp = *x;
300///     *x = *y;
301///     *y = temp;
302///     *x + *y
303/// };
304/// let multiply = |x: &mut i32, y: &mut i32| {
305///     *x *= *y;
306///     *x
307/// };
308///
309/// let conditional = swap_and_sum.when(|x: &mut i32, y: &mut i32| *x > 0 && *y > 0).or_else(multiply);
310/// let mut a = 5;
311/// let mut b = 3;
312/// assert_eq!(conditional.apply(&mut a, &mut b), 8); // swap_and_sum executed
313///
314/// let conditional2 = swap_and_sum.when(|x: &mut i32, y: &mut i32| *x > 0 && *y > 0).or_else(multiply);
315/// let mut a = -5;
316/// let mut b = 3;
317/// assert_eq!(conditional2.apply(&mut a, &mut b), -15); // multiply executed
318/// ```
319///
320/// # Author
321///
322/// Haixing Hu
323pub trait FnBiMutatingFunctionOnceOps<T, U, R>: FnOnce(&mut T, &mut U) -> R + Sized {
324    /// Chain composition - applies self first, then after
325    ///
326    /// Creates a new bi-mutating-function that applies this bi-mutating-function first,
327    /// then applies the after function to the result. Consumes self and
328    /// returns a `BoxBiMutatingFunctionOnce`.
329    ///
330    /// # Type Parameters
331    ///
332    /// * `S` - The output type of the after function
333    /// * `F` - The type of the after function (must implement Function<R, S>)
334    ///
335    /// # Parameters
336    ///
337    /// * `after` - The function to apply after self. **Note: This parameter
338    ///   is passed by value and will transfer ownership.** Since this is a
339    ///   `FnOnce` bi-mutating-function, the parameter will be consumed. Can be:
340    ///   - A closure: `|x: R| -> S`
341    ///   - A function pointer: `fn(R) -> S`
342    ///   - A `BoxFunction<R, S>`
343    ///   - An `RcFunction<R, S>`
344    ///   - An `ArcFunction<R, S>`
345    ///   - Any type implementing `Function<R, S>`
346    ///
347    /// # Returns
348    ///
349    /// A new `BoxBiMutatingFunctionOnce<T, U, S>` representing the composition
350    ///
351    /// # Examples
352    ///
353    /// ```rust
354    /// use qubit_function::{BiMutatingFunctionOnce, FnBiMutatingFunctionOnceOps,
355    ///     BoxFunction};
356    ///
357    /// let swap_and_sum = |x: &mut i32, y: &mut i32| {
358    ///     let temp = *x;
359    ///     *x = *y;
360    ///     *y = temp;
361    ///     *x + *y
362    /// };
363    /// let to_string = BoxFunction::new(|x: i32| x.to_string());
364    ///
365    /// // to_string is moved and consumed
366    /// let composed = swap_and_sum.and_then(to_string);
367    /// let mut a = 20;
368    /// let mut b = 22;
369    /// assert_eq!(composed.apply(&mut a, &mut b), "42");
370    /// // to_string.apply(10); // Would not compile - moved
371    /// ```
372    fn and_then<S, F>(self, after: F) -> BoxBiMutatingFunctionOnce<T, U, S>
373    where
374        Self: 'static,
375        S: 'static,
376        F: crate::functions::function::Function<R, S> + 'static,
377        T: 'static,
378        U: 'static,
379        R: 'static,
380    {
381        BoxBiMutatingFunctionOnce::new(move |t: &mut T, u: &mut U| after.apply(&self(t, u)))
382    }
383
384    /// Creates a conditional bi-mutating-function
385    ///
386    /// Returns a bi-mutating-function that only executes when a bi-predicate is
387    /// satisfied. You must call `or_else()` to provide an alternative
388    /// bi-mutating-function for when the condition is not satisfied.
389    ///
390    /// # Parameters
391    ///
392    /// * `predicate` - The condition to check. **Note: This parameter is passed
393    ///   by value and will transfer ownership.** If you need to preserve the
394    ///   original bi-predicate, clone it first (if it implements `Clone`).
395    ///   Can be:
396    ///   - A closure: `|x: &mut T, y: &mut U| -> bool`
397    ///   - A function pointer: `fn(&mut T, &mut U) -> bool`
398    ///   - A `BoxBiPredicate<T, U>`
399    ///   - An `RcBiPredicate<T, U>`
400    ///   - An `ArcBiPredicate<T, U>`
401    ///   - Any type implementing `BiPredicate<T, U>`
402    ///
403    /// # Returns
404    ///
405    /// Returns `BoxConditionalBiMutatingFunctionOnce<T, U, R>`
406    ///
407    /// # Examples
408    ///
409    /// ## Basic usage with or_else
410    ///
411    /// ```rust
412    /// use qubit_function::{BiMutatingFunctionOnce, FnBiMutatingFunctionOnceOps};
413    ///
414    /// let swap_and_sum = |x: &mut i32, y: &mut i32| {
415    ///     let temp = *x;
416    ///     *x = *y;
417    ///     *y = temp;
418    ///     *x + *y
419    /// };
420    /// let multiply = |x: &mut i32, y: &mut i32| {
421    ///     *x *= *y;
422    ///     *x
423    /// };
424    /// let conditional = swap_and_sum.when(|x: &mut i32, y: &mut i32| *x > 0)
425    ///     .or_else(multiply);
426    ///
427    /// let mut a = 5;
428    /// let mut b = 3;
429    /// assert_eq!(conditional.apply(&mut a, &mut b), 8);
430    /// ```
431    ///
432    /// ## Preserving bi-predicate with clone
433    ///
434    /// ```rust
435    /// use qubit_function::{BiMutatingFunctionOnce, FnBiMutatingFunctionOnceOps,
436    ///     RcBiPredicate};
437    ///
438    /// let swap_and_sum = |x: &mut i32, y: &mut i32| {
439    ///     let temp = *x;
440    ///     *x = *y;
441    ///     *y = temp;
442    ///     *x + *y
443    /// };
444    /// let both_positive = RcBiPredicate::new(|x: &mut i32, y: &mut i32|
445    ///     *x > 0 && *y > 0);
446    ///
447    /// // Clone to preserve original bi-predicate
448    /// let conditional = swap_and_sum.when(both_positive.clone())
449    ///     .or_else(|x: &mut i32, y: &mut i32| *x * *y);
450    ///
451    /// let mut a = 5;
452    /// let mut b = 3;
453    /// assert_eq!(conditional.apply(&mut a, &mut b), 8);
454    ///
455    /// // Original bi-predicate still usable
456    /// let mut test_a = 5;
457    /// let mut test_b = 3;
458    /// assert!(both_positive.test(&mut test_a, &mut test_b));
459    /// ```
460    fn when<P>(self, predicate: P) -> BoxConditionalBiMutatingFunctionOnce<T, U, R>
461    where
462        Self: 'static,
463        P: BiPredicate<T, U> + 'static,
464        T: 'static,
465        U: 'static,
466        R: 'static,
467    {
468        BoxBiMutatingFunctionOnce::new(self).when(predicate)
469    }
470}
471
472/// Blanket implementation of FnBiMutatingFunctionOnceOps for all closures
473///
474/// Automatically implements `FnBiMutatingFunctionOnceOps<T, U, R>` for any type that
475/// implements `FnOnce(&mut T, &mut U) -> R`.
476///
477/// # Author
478///
479/// Haixing Hu
480impl<T, U, R, F> FnBiMutatingFunctionOnceOps<T, U, R> for F
481where
482    F: FnOnce(&mut T, &mut U) -> R,
483{
484    // empty
485}
486
487// ============================================================================
488// BoxConditionalBiMutatingFunctionOnce - Box-based Conditional BiMutatingFunction
489// ============================================================================
490
491/// BoxConditionalBiMutatingFunctionOnce struct
492///
493/// A conditional consuming bi-mutating-function that only executes when a bi-predicate
494/// is satisfied. Uses `BoxBiMutatingFunctionOnce` and `BoxBiPredicate` for single
495/// ownership semantics.
496///
497/// This type is typically created by calling `BoxBiMutatingFunctionOnce::when()` and
498/// is designed to work with the `or_else()` method to create if-then-else logic.
499///
500/// # Features
501///
502/// - **Single Ownership**: Not cloneable, consumes `self` on use
503/// - **One-time Use**: Can only be called once
504/// - **Conditional Execution**: Only computes when bi-predicate returns `true`
505/// - **Chainable**: Can add `or_else` branch to create if-then-else logic
506///
507/// # Author
508///
509/// Haixing Hu
510pub struct BoxConditionalBiMutatingFunctionOnce<T, U, R> {
511    function: BoxBiMutatingFunctionOnce<T, U, R>,
512    predicate: BoxBiPredicate<T, U>,
513}
514
515// Implement BoxConditionalBiMutatingFunctionOnce
516impl_box_conditional_function!(
517    BoxConditionalBiMutatingFunctionOnce<T, U, R>,
518    BoxBiMutatingFunctionOnce,
519    BiMutatingFunctionOnce
520);
521
522// Use macro to generate Debug and Display implementations
523impl_conditional_function_debug_display!(BoxConditionalBiMutatingFunctionOnce<T, U, R>);