Skip to main content

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