Use a closure in a struct

Four key reasons to use a closure in a struct:

  1. Encapsulating Behavior: Closures allow you to package specific behaviors or operations with the data they act upon, creating a more modular and encapsulated design.
  2. Customizable and Dynamic Behavior: Using closures makes it easy to change or customize the behavior of a struct instance without needing to define multiple types or implementations.
  3. Dependency Injection: Closures enable dependency injection, allowing you to provide different implementations of a behavior at runtime, which is useful for testing and separating configuration from implementation.
  4. Reducing Boilerplate: Closures can simplify your code by reducing the need for multiple trait implementations or separate function definitions, making the codebase cleaner and easier to maintain.
///using a closure in a struct

struct ClosStruct<F>
    F: Fn(i32) -> i32,
    doubled: F,

fn main() {
    // create closure
    let cl_double = |n: i32| n * 2;
    // instantiate struct
    let clst = ClosStruct { doubled: cl_double };
    // execute
    println!("{:?}", (clst.doubled)(4));

Dependency injection example

This example demonstrates how to use closures for dependency injection, providing flexibility to inject different behaviours into a struct at runtime.

struct Calculator<F>
    F: Fn(i32, i32) -> i32,
    calculate: F,

fn main() {
    // Define closures for different operations
    let add = |a, b| a + b;
    let multiply = |a, b| a * b;

    // Instantiate the Calculator struct with different operations
    let add_calculator = Calculator { calculate: add };
    let multiply_calculator = Calculator { calculate: multiply };

    // Use the calculators
    let sum = (add_calculator.calculate)(2, 3);
    let product = (multiply_calculator.calculate)(2, 3);

    // Print the results
    println!("Sum: {}", sum);         // Output: Sum: 5
    println!("Product: {}", product); // Output: Product: 6