Lambda y Closures

C# y Rust permiten que las funciones sean utilizadas como valores de primera clase que posibilitan la escritura de funciones de orden superior. Las funciones de orden superior son esencialmente funciones que aceptan otras funciones como argumentos para permitir que el llamador participe en el código de la función llamada. En C#, los function pointers seguros se representan mediante delegados, siendo los más comunes Func y Action. El lenguaje C# permite la creación de instancias ad hoc de estos delegados a través de expresiones lambda.

Rust también tiene function pointers, siendo el tipo más simple fn:

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
}

Sin embargo, Rust hace una distinción entre funciones punteros (donde fn define un tipo) y closures: una closure puede hacer referencia a variables desde su ámbito léxico circundante, pero no una función puntero. Aunque C# también tiene function pointers (*delegate), el equivalente gestionado y seguro para el tipo sería una expresión lambda estática.

Las funciones y métodos que aceptan closures se escriben con tipos genéricos que está vinculado a uno de los traits que representa funciones: Fn, FnMut y FnOnce. Cuando es el momento de proporcionar un valor para un puntero a función o un cierre, un desarrollador de Rust utiliza una closure expression (como |x| x + 1 en el ejemplo anterior), que se traduce de la misma manera que una expresión lambda en C#. Si la closure expression crea una pointer function
o una closure depende de si la closure expression hace referencia a su contexto o no.

Cuando una closure captura variables de su entorno, entran en juego las reglas de ownership porque el ownership termina en la closure. Para obtener más información, consulta la sección "Moviendo valores capturados fuera de los closures y los traits Fn" de "El Lenguaje de Programación Rust".