Lambda and Closures

C# and Rust allow functions to be used as first-class values that enable writing higher-order functions. Higher-order functions are essentially functions that accept other functions as arguments to allow for the caller to participate in the code of the called function. In C#, type-safe function pointers are represented by delegates with the most common ones being Func and Action. The C# language allows ad-hoc instances of these delegates to be created through lambda expressions.

Rust has function pointers too with the fn type being the simplest:

fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
    f(arg) + f(arg)
}

fn main() {
    let answer = do_twice(|x| x + 1, 5);
    println!("The answer is: {}", answer); // Prints: The answer is: 12
}

However, Rust makes a distinction between function pointers (where fn defines a type) and closures: a closure can reference variables from its surrounding lexical scope, but not a function pointer. While C# also has function pointers (*delegate), the managed and type-safe equivalent would be a static lambda expression.

Functions and methods that accept closures are written with generic types that are bound to one of the traits representing functions: Fn, FnMut and FnOnce. When it's time to provide a value for a function pointer or a closure, a Rust developer uses a closure expression (like |x| x + 1 in the example above), which translates to the same as a lambda expression in C#. Whether the closure expression creates a function pointer or a closure depends on whether the closure expression references its context or not.

When a closure captures variables from its environment then ownership rules come into play because the ownership ends up with the closure. For more information, see the “Moving Captured Values Out of Closures and the Fn Traits” section of The Rust Programming Language.