2015-11-09 18 views
5

Ricevo un errore in un lambda annidato.Errore di controllo del prestito con variabile che non vive abbastanza a lungo in lambda annidato

let rows = vec![ 
    vec![3, 6, 2, 8, 9, 0], 
    vec![0, 0, 1, 4, 5, 1], 
]; 

let pair_sums = rows.iter() 
    .flat_map(|row| { 
     (0..row.len() - 1).map(|i| row[i] + row[i + 1]) 
    }) 
    .collect::<Vec<_>>(); 

println!("{:?}", pair_sums); 
error[E0597]: `row` does not live long enough 
    --> src/main.rs:9:40 
    | 
9 |    (0..row.len() - 1).map(|i| row[i] + row[i + 1]) 
    |         --- ^^^ does not live long enough 
    |         | 
    |         capture occurs here 
10 |   }) 
    |   - borrowed value only lives until here 
11 |   .collect::<Vec<_>>(); 
    |       - borrowed value needs to live until here 

posso sorta di capire perché questo sta accadendo, e posso risolvere il problema facendo passare il valore di row fino alla lambda interna:

let pair_sums = rows.iter() 
    .flat_map(|row| { 
     (0..row.len() - 1).zip(vec![row; row.len()]) 
      .map(|(i, row)| row[i] + row[i + 1]) 
    }) 
    .collect::<Vec<_>>(); 

Questo è orribile e può' essere la migliore soluzione Come posso fare riferimento a variabili in ambito genitore senza doverle passare esplicitamente?

risposta

7

Il trucco qui è come le chiusure catturano le loro variabili: prenderanno dei riferimenti a loro se è consentito dal contenuto della chiusura, senza guardare come vengono utilizzate, per mantenere l'inferenza locale verso l'espressione di chiusura e prevedibile . In questo caso la variabile row viene sempre usata solo come riferimento, quindi va bene essere catturata per riferimento; cioè, l'oggetto di chiusura passato alla mappa contiene un riferimento a row. Pertanto, questo oggetto non può lasciare l'ambito che dichiara la variabile row (ovvero la chiusura di flat_map) perché tale riferimento verrà lasciato puntare a una memoria non valida. Restituire .map(closure) fallirà questa regola, poiché .map crea un iteratore pigro che memorizza la chiusura e la chiama solo quando gli elementi vengono richiesti.

La correzione qui è di forzare la variabile row a non essere catturata dal riferimento, in modo che la chiusura possa lasciare l'ambito. Questo può essere fatto con la parola chiave move:

let pair_sums = rows.iter() 
    .flat_map(|row| { 
     (0..row.len() - 1) 
      .map(move |i| row[i] + row[i + 1]) 
    }) 
    .collect::<Vec<_>>(); 

In altre parole, il codice originale è equivalente a qualcosa come:

let pair_sums = rows.iter() 
    .flat_map(|row: &Vec<i32>| { 
     let row_ref: &&Vec<i32> = &row; 
     (0..row.len() - 1) 
      .map(move |i| (*row_ref)[i] + (*row_ref)[i + 1]) 
    }) 
    .collect::<Vec<_>>(); 

(mio Finding Closure in Rust posta scava nel chiusure in modo più dettagliato, così come la Rust book.)

+0

Grazie. Ho appena riletto questa risposta dopo alcuni mesi. La parte che non era ovvia è l'iteratore della mappa pigra. Collegarlo con la cattura per riferimento rende tutto molto sensato. –

Problemi correlati