Funciones como argumentos en Rust: 3 formas de pasar una función

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Función como argumento en Rust: de principiante a profesional

\n\n

¿Alguna vez ha escrito en Python o JavaScript y ha pasado una función a otra función con facilidad? ¿Y luego pasó a Rust y tropezó en terreno plano? Familiar. La pregunta "How do you pass a Rust function as a parameter?" es una de las más populares en Stack Overflow. Y con razón: el sistema de tipos y propiedad de Rust añade varias capas de complejidad, pero también abre nuevas posibilidades.

\n\n

En este artículo, analizaremos tres enfoques principales: punteros a funciones (fn), closures (closures) y traits. Aprenderá cuándo usar cada uno y dejará de temer al estilo funcional en Rust.

\n\n

¿Por qué es importante?

\n

La capacidad de pasar funciones como parámetros no es solo un truco académico. Es la base para:

\n
    \n
  • Crear bibliotecas flexibles y reutilizables (por ejemplo, iteradores, manejadores de eventos);
  • \n
  • Implementar los patrones "Estrategia" y "Observador";
  • \n
  • Escribir código conciso y expresivo sin montones de if-else.
  • \n
\n

En Rust, a diferencia de los lenguajes dinámicos, el compilador verifica estrictamente los tipos y los tiempos de vida. Por lo tanto, debe comprender claramente qué está pasando: solo una dirección de función, un closure con captura de contexto o algo más complejo.

\n\n

Enfoque 1: Puntero a función (Function Pointer) — fn

\n

La forma más simple y directa. Si su función no captura variables del entorno externo (es "pura"), puede usar el tipo fn. Tenga en cuenta: fn es un tipo, mientras que Fn, FnMut, FnOnce son traits. ¡No los confunda!

\n\n
fn add_one(x: i32) -> i32 {\n    x + 1\n}\n\n// Función que recibe otra función\nfn apply_twice(f: fn(i32) -> i32, value: i32) -> i32 {\n    f(f(value))\n}\n\nfn main() {\n    let result = apply_twice(add_one, 5);\n    println!("Resultado: {}", result); // 7\n}
\n\n

¿Cuándo usarlo? Cuando la función se conoce en tiempo de compilación, no captura variables y no necesita el rendimiento de los closures. Los punteros a funciones son una abstracción de costo cero, no requieren asignaciones.

\n\n

Limitaciones de fn

\n

La principal desventaja: no puede pasar un closure que capture variables. El siguiente código no compilará:

\n\n
fn main() {\n    let y = 10;\n    // El closure captura y\n    let closure = |x: i32| -> i32 { x + y };\n    // Error! expected fn item, found closure\n    // let result = apply_twice(closure, 5);\n}
\n\n

Para esto se necesitan los traits Fn, FnMut, FnOnce.

\n\n

Enfoque 2: Closures y traits Fn, FnMut, FnOnce

\n

Rust es un lenguaje de sistemas y necesita saber qué sucede con la memoria. Los closures pueden capturar variables de tres maneras:

\n
    \n
  • Por referencia (&T) — trait Fn. Se puede llamar tantas veces como se quiera, sin modificar los datos capturados.
  • \n
  • Por referencia mutable (&mut T) — trait FnMut. Se puede llamar muchas veces, pero los datos pueden modificarse.
  • \n
  • Por valor (T) — trait FnOnce. Solo se puede llamar una vez, después de lo cual el closure se "consume".
  • \n
\n\n

Ejemplo con Fn

\n
fn apply_twice_fn<F>(f: F, value: i32) -> i32\nwhere\n    F: Fn(i32) -> i32,\n{\n    f(f(value))\n}\n\nfn main() {\n    let y = 10;\n    let closure = |x| x + y; // captura y por referencia\n    let result = apply_twice_fn(closure, 5);\n    println!("Fn: {}", result); // 25\n}
\n\n

Ejemplo con FnMut

\n
fn apply_twice_mut<F>(mut f: F, value: i32) -> i32\nwhere\n    F: FnMut(i32) -> i32,\n{\n    f(f(value))\n}\n\nfn main() {\n    let mut counter = 0;\n    let mut closure = |x: i32| -> i32 {\n                

Blogs

Book Recommendations