Skip to main content

qubit_function/functions/bi_mutating_function/
fn_bi_mutating_function_ops.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! Defines the `FnBiMutatingFunctionOps` public type.
10
11#![allow(unused_imports)]
12
13use super::*;
14
15// ============================================================================
16// FnBiMutatingFunctionOps - Extension trait for Fn(&mut T, &mut U) -> R bi-functions
17// ============================================================================
18
19/// Extension trait for closures implementing `Fn(&mut T, &mut U) -> R`
20///
21/// Provides composition methods (`and_then`, `when`) for bi-mutating-function
22/// closures and function pointers without requiring explicit wrapping in
23/// `BoxBiMutatingFunction`.
24///
25/// This trait is automatically implemented for all closures and function
26/// pointers that implement `Fn(&mut T, &mut U) -> R`.
27///
28/// # Design Rationale
29///
30/// While closures automatically implement `BiMutatingFunction<T, U, R>` through
31/// blanket implementation, they don't have access to instance methods like
32/// `and_then` and `when`. This extension trait provides those methods,
33/// returning `BoxBiMutatingFunction` for maximum flexibility.
34///
35/// # Examples
36///
37/// ## Chain composition with and_then
38///
39/// ```rust
40/// use qubit_function::{BiMutatingFunction, FnBiMutatingFunctionOps};
41///
42/// let swap_and_sum = |x: &mut i32, y: &mut i32| {
43///     let temp = *x;
44///     *x = *y;
45///     *y = temp;
46///     *x + *y
47/// };
48/// let double = |x: &i32| x * 2;
49///
50/// let composed = swap_and_sum.and_then(double);
51/// let mut a = 3;
52/// let mut b = 5;
53/// assert_eq!(composed.apply(&mut a, &mut b), 16); // (5 + 3) * 2 = 16
54/// ```
55///
56/// ## Conditional execution with when
57///
58/// ```rust
59/// use qubit_function::{BiMutatingFunction, FnBiMutatingFunctionOps};
60///
61/// let swap_and_sum = |x: &mut i32, y: &mut i32| {
62///     let temp = *x;
63///     *x = *y;
64///     *y = temp;
65///     *x + *y
66/// };
67/// let multiply = |x: &mut i32, y: &mut i32| {
68///     *x *= *y;
69///     *x
70/// };
71///
72/// let conditional = swap_and_sum.when(|x: &i32, y: &i32| *x > 0 && *y > 0).or_else(multiply);
73///
74/// let mut a = 5;
75/// let mut b = 3;
76/// assert_eq!(conditional.apply(&mut a, &mut b), 8);   // swap_and_sum: (3 + 5)
77///
78/// let mut a = -5;
79/// let mut b = 3;
80/// assert_eq!(conditional.apply(&mut a, &mut b), -15); // multiply: (-5 * 3)
81/// ```
82///
83/// # Author
84///
85/// Haixing Hu
86pub trait FnBiMutatingFunctionOps<T, U, R>: Fn(&mut T, &mut U) -> R + Sized {
87    /// Chain composition - applies self first, then after
88    ///
89    /// Creates a new bi-mutating-function that applies this bi-mutating-function first,
90    /// then applies the after function to the result. Consumes self and
91    /// returns a `BoxBiMutatingFunction`.
92    ///
93    /// # Type Parameters
94    ///
95    /// * `S` - The output type of the after function
96    /// * `F` - The type of the after function (must implement Function<R, S>)
97    ///
98    /// # Parameters
99    ///
100    /// * `after` - The function to apply after self. **Note: This parameter
101    ///   is passed by value and will transfer ownership.** If you need to
102    ///   preserve the original function, clone it first (if it implements
103    ///   `Clone`). Can be:
104    ///   - A closure: `|x: R| -> S`
105    ///   - A function pointer: `fn(R) -> S`
106    ///   - A `BoxFunction<R, S>`
107    ///   - An `RcFunction<R, S>`
108    ///   - An `ArcFunction<R, S>`
109    ///   - Any type implementing `Function<R, S>`
110    ///
111    /// # Returns
112    ///
113    /// A new `BoxBiMutatingFunction<T, U, S>` representing the composition
114    ///
115    /// # Examples
116    ///
117    /// ## Direct value passing (ownership transfer)
118    ///
119    /// ```rust
120    /// use qubit_function::{BiMutatingFunction, FnBiMutatingFunctionOps,
121    ///     BoxFunction, Function};
122    ///
123    /// let swap = |x: &mut i32, y: &mut i32| {
124    ///     let temp = *x;
125    ///     *x = *y;
126    ///     *y = temp;
127    ///     *x + *y
128    /// };
129    /// let to_string = BoxFunction::new(|x: &i32| x.to_string());
130    ///
131    /// // to_string is moved here
132    /// let composed = swap.and_then(to_string);
133    /// let mut a = 20;
134    /// let mut b = 22;
135    /// assert_eq!(composed.apply(&mut a, &mut b), "42");
136    /// // to_string.apply(10); // Would not compile - moved
137    /// ```
138    ///
139    /// ## Preserving original with clone
140    ///
141    /// ```rust
142    /// use qubit_function::{BiMutatingFunction, FnBiMutatingFunctionOps,
143    ///     Function, RcFunction};
144    ///
145    /// let swap = |x: &mut i32, y: &mut i32| {
146    ///     let temp = *x;
147    ///     *x = *y;
148    ///     *y = temp;
149    ///     *x + *y
150    /// };
151    /// let to_string = RcFunction::new(|x: &i32| x.to_string());
152    ///
153    /// // Clone to preserve original
154    /// let composed = swap.and_then(to_string.clone());
155    /// let mut a = 20;
156    /// let mut b = 22;
157    /// assert_eq!(composed.apply(&mut a, &mut b), "42");
158    ///
159    /// // Original still usable
160    /// assert_eq!(to_string.apply(&10), "10");
161    /// ```
162    fn and_then<S, F>(self, after: F) -> BoxBiMutatingFunction<T, U, S>
163    where
164        Self: 'static,
165        S: 'static,
166        F: crate::functions::function::Function<R, S> + 'static,
167        T: 'static,
168        U: 'static,
169        R: 'static,
170    {
171        BoxBiMutatingFunction::new(move |t: &mut T, u: &mut U| after.apply(&self(t, u)))
172    }
173
174    /// Creates a conditional bi-mutating-function
175    ///
176    /// Returns a bi-mutating-function that only executes when a bi-predicate is
177    /// satisfied. You must call `or_else()` to provide an alternative
178    /// bi-mutating-function for when the condition is not satisfied.
179    ///
180    /// # Parameters
181    ///
182    /// * `predicate` - The condition to check. **Note: This parameter is passed
183    ///   by value and will transfer ownership.** If you need to preserve the
184    ///   original bi-predicate, clone it first (if it implements `Clone`).
185    ///   Can be:
186    ///   - A closure: `|x: &mut T, y: &mut U| -> bool`
187    ///   - A function pointer: `fn(&mut T, &mut U) -> bool`
188    ///   - A `BoxBiPredicate<T, U>`
189    ///   - An `RcBiPredicate<T, U>`
190    ///   - An `ArcBiPredicate<T, U>`
191    ///   - Any type implementing `BiPredicate<T, U>`
192    ///
193    /// # Returns
194    ///
195    /// Returns `BoxConditionalBiMutatingFunction<T, U, R>`
196    ///
197    /// # Examples
198    ///
199    /// ## Basic usage with or_else
200    ///
201    /// ```rust
202    /// use qubit_function::{BiMutatingFunction, FnBiMutatingFunctionOps};
203    ///
204    /// let swap_and_sum = |x: &mut i32, y: &mut i32| {
205    ///     let temp = *x;
206    ///     *x = *y;
207    ///     *y = temp;
208    ///     *x + *y
209    /// };
210    /// let multiply = |x: &mut i32, y: &mut i32| {
211    ///     *x *= *y;
212    ///     *x
213    /// };
214    /// let conditional = swap_and_sum.when(|x: &i32, y: &i32| *x > 0)
215    ///     .or_else(multiply);
216    ///
217    /// let mut a = 5;
218    /// let mut b = 3;
219    /// assert_eq!(conditional.apply(&mut a, &mut b), 8);
220    /// ```
221    ///
222    /// ## Preserving bi-predicate with clone
223    ///
224    /// ```rust
225    /// use qubit_function::{BiMutatingFunction, FnBiMutatingFunctionOps,
226    ///     RcBiPredicate};
227    ///
228    /// let swap_and_sum = |x: &mut i32, y: &mut i32| {
229    ///     let temp = *x;
230    ///     *x = *y;
231    ///     *y = temp;
232    ///     *x + *y
233    /// };
234    /// let both_positive = RcBiPredicate::new(|x: &i32, y: &i32|
235    ///     *x > 0 && *y > 0);
236    ///
237    /// // Clone to preserve original bi-predicate
238    /// let conditional = swap_and_sum.when(both_positive.clone())
239    ///     .or_else(|x: &mut i32, y: &mut i32| *x * *y);
240    ///
241    /// let mut a = 5;
242    /// let mut b = 3;
243    /// assert_eq!(conditional.apply(&mut a, &mut b), 8);
244    ///
245    /// // Original bi-predicate still usable
246    /// use qubit_function::BiPredicate;
247    /// let test_a = 5;
248    /// let test_b = 3;
249    /// assert!(both_positive.test(&test_a, &test_b));
250    /// ```
251    fn when<P>(self, predicate: P) -> BoxConditionalBiMutatingFunction<T, U, R>
252    where
253        Self: 'static,
254        P: BiPredicate<T, U> + 'static,
255        T: 'static,
256        U: 'static,
257        R: 'static,
258    {
259        BoxBiMutatingFunction::new(self).when(predicate)
260    }
261}
262
263/// Blanket implementation of FnBiMutatingFunctionOps for all closures
264///
265/// Automatically implements `FnBiMutatingFunctionOps<T, U, R>` for any type that
266/// implements `Fn(&mut T, &mut U) -> R`.
267///
268/// # Author
269///
270/// Haixing Hu
271impl<T, U, R, F> FnBiMutatingFunctionOps<T, U, R> for F where F: Fn(&mut T, &mut U) -> R {}