Devolución de un cierre de una función

Nota : Esta pregunta se hizo antes de la primera versión estable de Rust. Ha habido muchos cambios desde entonces y la syntax utilizada en la función ya no es válida. Aún así, la respuesta de Shepmaster es excelente y hace que esta pregunta valga la pena.


Finalmente, los cierres sin caja han aterrizado, por lo que estoy experimentando con ellos para ver qué puedes hacer.

Tengo esta simple función:

fn make_adder(a: int, b: int) -> || -> int { || a + b } 

Sin embargo, me sale un error de missing lifetime specifier [E0106] . He intentado solucionar esto cambiando el tipo de devolución a ||: 'static -> int , pero luego aparece otro error cannot infer an appropriate lifetime due to conflicting requirements .

Si lo entiendo correctamente, el cierre está sin caja por lo que posee a y b . Me parece muy extraño que necesite toda una vida. ¿Cómo puedo arreglar esto?

A partir de Rust 1.26, puedes usar el impl trait :

 fn make_adder(a: i32) -> impl Fn(i32) -> i32 { move |b| a + b } fn main() { println!("{}", make_adder(1)(2)); } 

Esto permite devolver un cierre sin caja a pesar de que es imposible especificar el tipo exacto de cierre.

Esto no te ayudará si estás apuntando a Rust antes de esta versión o si tienes algún tipo de condición en tu función:

 fn make_adder(a: i32) -> impl Fn(i32) -> i32 { if a > 0 { move |b| a + b } else { move |b| a - b } } 

Aquí, no hay un solo tipo de devolución; cada cierre tiene un tipo único, no identificable. En este caso, debe usar indirección. La solución común es un objeto de rasgo , como se describe en la otra respuesta .

Es posible devolver cierres dentro de Box es, es decir, como objetos de rasgo que implementan ciertos rasgos:

 fn make_adder(a: i32) -> Box i32> { Box::new(move |b| a + b) } fn main() { println!("{}", make_adder(1)(2)); } 

(inténtalo aquí )

También hay un RFC ( su problema de seguimiento ) sobre cómo agregar tipos de retorno abstractos no empaquetados que permitirían devolver cierres por valor, sin casillas, pero este RFC se pospuso. De acuerdo con la discusión en ese RFC, parece que se ha hecho algún trabajo recientemente, por lo que es posible que los tipos de devolución abstracta sin caja estén disponibles relativamente pronto.

El || la syntax sigue siendo los viejos cierres en caja, por lo que esto no funciona por la misma razón que antes.

Y, no funcionará incluso con la syntax de cierre en caja correcta |&:| -> int |&:| -> int , ya que literalmente es solo azúcar para ciertos rasgos. Por el momento, la syntax del azúcar es |X: args...| -> ret |X: args...| -> ret , donde X puede ser & , &mut o nada, correspondiente a los rasgos Fn , FnMut , FnOnce , también puede escribir Fn< (args...), ret> etc. para la forma no azucarada. Es probable que el azúcar esté cambiando (posiblemente algo así como Fn(args...) -> ret ).

Cada cierre unboxed tiene un tipo único e innombrable generado internamente por el comstackdor: la única manera de hablar sobre cierres sin caja es a través de límites generics y de rasgo. En particular, escribir

 fn make_adder(a: int, b: int) -> |&:| -> int { |&:| a + b } 

es como escribir

 fn make_adder(a: int, b: int) -> Fn< (), int> { |&:| a + b } 

es decir, decir que make_adder devuelve un valor de rasgo sin caja; que no tiene mucho sentido en este momento. Lo primero que debes probar es

 fn make_adder>(a: int, b: int) -> F { |&:| a + b } 

pero esto está diciendo que make_adder está devolviendo cualquier F que la persona que llama elige, aunque queremos decir que devuelve un tipo fijo (pero “oculto”). Esto requirió tipos de retorno abstractos , que dice, básicamente, que “el valor de retorno implementa este rasgo” mientras se sigue desempacando y se resuelve estáticamente. En el lenguaje de ese (temporalmente cerrado) RFC,

 fn make_adder(a: int, b: int) -> impl Fn< (), int> { |&:| a + b } 

O con el azúcar de cierre.

(Otro punto menor: no estoy 100% seguro acerca de los cierres sin caja, pero los viejos cierres aún capturan cosas por referencia, que es otra cosa que hunde el código como se propone en el problema. Esto se está corrigiendo en el # 16610 ).