Closures in Rust: Static vs. Dynamic Dispatch with impl Fn and Box

Comparison: impl Fn vs Box<dyn Fn>


1. Using impl Fn (Static Dispatch)

fn create_multiplier(factor: f32) -> impl Fn(f32) -> f32 {
    move |value| value * factor
}

fn main() {
    let multiply_by_4 = create_multiplier(4.0);  // Closure that multiplies by 4
    let result = multiply_by_4(9.0);              // 9 * 4 = 36
    println!("Result: {:?}", result);             // Output: 36.0
}

Explanation:

  • create_multiplier returns a closure that multiplies input by factor.
  • impl Fn tells the compiler to use static dispatch, which is efficient.
  • The closure captures factor using move.

2. Using Box<dyn Fn> (Dynamic Dispatch)

fn create_dynamic_multiplier(factor: f32) -> Box<dyn Fn(f32) -> f32> {
    Box::new(move |value| value * factor)
}

fn main() {
    let multiply_by_4 = create_dynamic_multiplier(4.0);  // Closure that multiplies by 4
    let result = multiply_by_4(9.0);                      // 9 * 4 = 36
    println!("Result: {:?}", result);                     // Output: 36.0
}

Explanation:

  • create_dynamic_multiplier returns a boxed trait object (Box<dyn Fn>).
  • The closure is stored on the heap, allowing dynamic dispatch at runtime.
  • Useful when flexibility is needed for different closure types.

Key Differences:

Featureimpl Fn (Static Dispatch)Box<dyn Fn> (Dynamic Dispatch)
Dispatch TypeCompile-time (Static Dispatch)Run-time (Dynamic Dispatch)
PerformanceFaster (No heap allocation)Slower (Heap allocation + Indirection)
FlexibilityLess flexible (Fixed closure type)More flexible (Can store different types)
Memory UsageNo heap usageUses heap (Boxed closure)
Use CaseWhen closure type is known at compile timeWhen closure types may vary dynamically

When to Use Each:

  1. impl Fn:
    • When you know the closure type at compile time.
    • Performance-critical code (no heap allocation).
  2. Box<dyn Fn>:
    • When you need flexibility (e.g., storing multiple closures).
    • Useful for dynamically created closures or heterogeneous collections.

Summary Example:

fn main() {
    // Static dispatch: Fast and efficient
    let static_multiplier = create_multiplier(3.0);
    println!("Static Result: {}", static_multiplier(10.0));  // Output: 30.0

    // Dynamic dispatch: More flexible
    let dynamic_multiplier = create_dynamic_multiplier(2.5);
    println!("Dynamic Result: {}", dynamic_multiplier(8.0)); // Output: 20.0
}

AI ML

Previous article

Qdrant & Rust – embeddingsNew!!