2016-03-01 11 views
8

Sto provando a scrivere una funzione che restituisce una chiusura in scatola che può funzionare su riferimenti a tipi con qualsiasi durata di vita . Quando scrivi un'istanza specifica, tutto funziona correttamente. Ma quando si scrive una versione generica , mi imbatto in problemi di durata.Numero più elevato tra i titoli vincolati e chiusure in scatola a vita

struct Parameter<'a> { 
    s: &'a str, 
} 

fn main() { 
    let closure = generate_closure_gen(); 
    let string = String::from("Hello World!"); 
    let parameter = Parameter { s: &string }; // Error: string does not live long enough 
    closure(&parameter); 
} 

// This one works fine 
// Desugared version for Box<Fn(&Parameter)> 
fn generate_closure() -> Box<for <'a, 'r> Fn(&'r Parameter<'a>)> { 
    Box::new(|c: &Parameter| {}) 
} 

// This one gives lifetime errors 
fn generate_closure_gen<C>() -> Box<Fn(&C)> { 
    Box::new(|c: &C| {}) 
} 

non vedo il motivo per cui la chiusura ha bisogno il parametro di tipo a vivere più a lungo di quanto non (non c'è nessun deposito o niente ...). E funziona per la versione non generica con HRTB, sembra proprio che dovrebbe essere possibile farlo funzionare con la versione generica.
Inoltre, se provo a scrivere la versione specifica utilizzando la versione generica, ottengo un errore di tipo

// Desugared version for Box<Fn(&Parameter)> 
fn generate_closure_2() -> Box<for <'a, 'r> Fn(&'r Parameter<'a>)> { 
    generate_closure_gen() 
} 

src/main.rs:22:5: 22:27 error: mismatched types: 
expected `Box<for<'r, 'r> core::ops::Fn(&'r Parameter<'r>) + 'static>`, 
    found `Box<for<'r> core::ops::Fn(&'r _) + 'static>` 
(expected concrete lifetime, 
    found bound lifetime parameter) [E0308] 
src/main.rs:22  generate_closure_gen() 
        ^~~~~~~~~~~~~~~~~~~~~~ 
src/main.rs:22:5: 22:27 help: run `rustc --explain E0308` to see a detailed explanation 

Qualche idea su come fare questo lavoro?

(playpen link)

+1

Il compilatore non sembra inserire 'per <'a>' da solo quando viene fornito un parametro generico. Se non si riceve alcuna spiegazione soddisfacente, è possibile che si desideri pubblicare un collegamento a questa domanda su https://www.reddit.com/r/rust/ dove gli sviluppatori di Rust hanno maggiori probabilità di rimanere in giro. –

+0

La mia risposta era ovviamente sbagliata, quindi l'ho cancellata. Ma il tuo commento sta spiegando che cosa intendi esattamente: "Non vedo perché la chiusura abbia bisogno del parametro tipo per vivere più a lungo di quello (non c'è spazio o spazio ...) e funziona per la versione non generica con HRTB, sembra proprio che dovrebbe essere possibile farlo funzionare con la versione generica. " - @Vaelden – aSpex

risposta

6

parametri del tipo hanno una durata legata. Questo limite di durata è il più breve di tutti i parametri di vita del implementatore. È omesso su generate_closure_gen, in modo che il compilatore dedurre, ma se abbiamo esplicitamente scritto fuori, la definizione della funzione sarebbe simile a questa:

fn generate_closure_gen<'a, C: 'a>() -> Box<Fn(&C)> { 
    Box::new(|c: &C| {}) 
} 

Questa modifica non risolve il nostro problema, però.

Per capire perché, è necessario capire che cosa si intende che sia C. Si chiama la chiusura con un &'y Parameter<'x> e la chiusura accetta for<'b> &'b C, quindi C è Parameter<'x>. Parameter<'x> ha un parametro di durata, che avrà un'influenza sulla durata limitata su C.

I parametri relativi alla durata in funzioni generiche devono essere sostituiti con una durata che inizia prima della chiamata di funzione. In questo caso, ciò significa che la durata di qualsiasi C che passiamo alla chiusura deve essere valida prima della chiamata a generate_closure_gen. Questo perché C è associato a una durata specifica, non a una vita intera; Ad esempio, quando C è Parameter<'x>, lo 'x deve essere noto in anticipo; non possiamo avere un diverso 'x ogni volta che chiamiamo la chiusura. In altre parole, quello che ci piacerebbe avere è qualcosa di simile:

fn generate_closure_gen<C: for<'a> 'a>() -> Box<Fn(&C)> { 
    Box::new(|c| {}) 
} 

Ma, purtroppo, questo non è legale di Rust 1.7.