Restituzione di una chiusura da una funzione

Nota : questa domanda è stata posta prima della prima versione stabile di Rust. Ci sono state molte modifiche da allora e la syntax utilizzata nella funzione non è più valida. Tuttavia, la risposta di Shepmaster è eccellente e rende questa domanda degna di essere tenuta.


Finalmente sono sbarcate le chiusure unboxed, quindi sto sperimentando con loro per vedere cosa puoi fare.

Ho questa semplice funzione:

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

Tuttavia, ottengo un errore del missing lifetime specifier [E0106] . Ho provato a risolvere questo problema cambiando il tipo di ritorno in ||: 'static -> int , ma poi ottengo un altro errore cannot infer an appropriate lifetime due to conflicting requirements .

Se capisco correttamente, la chiusura è unboxed quindi possiede a e b . Mi sembra molto strano che abbia bisogno di una vita. Come posso risolvere questo?

A partire da Rust 1.26, è ansible utilizzare il impl trait :

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

Ciò consente di restituire una chiusura non chiusa anche se è imansible specificare il tipo esatto della chiusura.

Questo non ti aiuterà se stai prendendo di mira Rust prima di questa versione o hai qualche tipo di condizionale nella tua funzione:

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

Qui, non c’è un singolo tipo di ritorno; ogni chiusura ha un tipo unico e non nominabile. In questo caso, è necessario utilizzare l’indirezione. La soluzione comune è un object tratto , come descritto nell’altra risposta .

È ansible restituire chiusure all’interno di Box es, ovvero come oggetti tratto che implementano determinati tratti:

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

(provalo qui )

C’è anche un RFC (il suo problema di tracciamento ) sull’aggiunta di tipi di reso astratti non archiviati che consentirebbero di restituire chiusure per valore, senza scatole, ma questa RFC è stata posticipata. Secondo la discussione in quel RFC, sembra che qualche lavoro sia stato fatto su di esso di recente, quindi è ansible che i tipi di ritorno astratto non inbox saranno disponibili relativamente presto.

|| la syntax è ancora la vecchia chiusura in scatola, quindi questo non funziona per la stessa ragione per cui non ha funzionato in precedenza.

E, non funzionerà nemmeno usando la syntax corretta della chiusura in scatola |&:| -> int |&:| -> int , dal momento che è letteralmente solo lo zucchero per determinati tratti. Al momento, la syntax dello zucchero è |X: args...| -> ret |X: args...| -> ret , dove X può essere & , &mut o nothing, corrispondente ai tratti Fn , FnMut , FnOnce , puoi anche scrivere Fn<(args...), ret> ecc. per la forma non zuccherata. Lo zucchero probabilmente cambierà (forse qualcosa come Fn(args...) -> ret ).

Ogni chiusura unboxed ha un tipo unico e non innestabile generato internamente dal compilatore: l’unico modo per parlare di chiusure non archiviate è tramite generici e limiti di tratti. In particolare, la scrittura

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

è come scrivere

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

cioè dicendo che make_adder restituisce un valore tratto non make_adder ; che non ha molto senso al momento. La prima cosa da provare sarebbe

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

ma questo sta dicendo che make_adder sta restituendo qualsiasi F dal chiamante , mentre vogliamo dire che restituisce un tipo fisso (ma “nascosto”). Ciò ha richiesto tipi di ritorno astratti , che in pratica dice “il valore di ritorno implementa questo tratto” pur rimanendo unboxed e staticamente risolto. Nella lingua di quella (temporaneamente chiusa) RFC,

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

O con lo zucchero di chiusura.

(Un altro punto secondario: non sono sicuro al 100% circa le chiusure unboxed, ma le vecchie chiusure catturano ancora le cose per riferimento che è un’altra cosa che affonda il codice come proposto nel problema. Questo è stato corretto in # 16610. )