Type anonymity

Closures succinctly capture variables from enclosing scopes. Does this have any consequences? It surely does. Observe how using a closure as a function parameter requires generics, which is necessary because of how they are defined:

// `F` must be generic.
fn foo<F, +Drop<F>, impl func: core::ops::FnOnce<F, ()>, +Drop<func::Output>>(f: F) {
    f();
}

When a closure is defined, the compiler implicitly creates a new anonymous structure to store the captured variables inside, meanwhile implementing the functionality via one of the traits: Fn or FnOnce for this unknown type. This type is assigned to the variable which is stored until calling.

Since this new type is of unknown type, any usage in a function will require generics. However, an unbounded type parameter <T> would still be ambiguous and not be allowed. Thus, bounding by one of the traits: Fn or FnOnce (which it implements) is sufficient to specify its type. Additional bounds are required to ensure that the closure can be called:

  • F must implement Drop (or Destruct) to go out of scope
  • func::Output, the output type of the closure, must implement Drop, as it is not used in the following code
// `F` must implement `Fn` for a closure which takes no
// inputs and returns nothing - exactly what is required
// for `print`.
// `func::Output` must implement `Drop` to ensure the closure's output is properly disposed of
// `F` must implement `Drop` to ensure the closure itself is properly disposed of
fn apply<F, +Drop<F>, impl func: core::ops::Fn<F, ()>, +Drop<func::Output>>(f: F) {
    f();
}

fn main() {
    let x = 7_u8;

    // Capture `x` into an anonymous type and implement
    // `Fn` for it. Store it in `print`.
    let print =  || println!("{}", x);

    // apply(print);
    print();
}

See also:

[A thorough analysis][thorough_analysis], Fn, and FnOnce