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