2015-11-09 18 views
5

Otrzymuję błąd wewnątrz zagnieżdżonej lambda.Błąd sprawdzania przy wypożyczeniu ze zmienną, która nie żyje wystarczająco długo w zagnieżdżonej lambda

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 

mogę rodzaju zobaczyć, dlaczego tak się dzieje, i mogę to naprawić przez gwintowania wartość row dzięki wewnętrznemu lambda:

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<_>>(); 

To jest straszne i może” t być najlepszym rozwiązaniem. Jak mogę odwoływać się do zmiennych w zakresie nadrzędnym bez konieczności bezpośredniego przekazywania ich dalej?

Odpowiedz

7

Chodzi o to, w jaki sposób zamknięcia przechwytują swoje zmienne: będą odwoływać się do nich, jeśli pozwala na to zawartość zamknięć, bez sprawdzania, w jaki sposób są one używane, aby zachować zgodność z lokalnym na wyrażenie zamknięcia i przewidywalne . W tym przypadku zmienna row jest używana tylko przez odniesienie, więc można przechwycić ją przez odniesienie; to znaczy obiekt zamknięcia przekazany do mapy zawiera odniesienie do row. Obiekt ten nie może zatem opuścić zakresu, który deklaruje zmienną row (to jest zamknięcie flat_map), ponieważ odniesienie to zostanie wskazane na niepoprawną pamięć. Powracanie .map(closure) będzie naruszać tę regułę, ponieważ .map tworzy leniwy iterator, który przechowuje zamknięcie i wywołuje je tylko w żądanych elementach.

Poprawka ma na celu wymuszenie, aby zmienna row nie była przechwytywana przez odniesienie, tak aby zamknięcie mogło opuścić zakres. Można to zrobić za pomocą słowa kluczowego move:

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

Innymi słowy, oryginalny kod jest równoznaczne z czymś takim:

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<_>>(); 

(Moja Finding Closure in Rust po kopie do zamknięć bardziej szczegółowo, jak to czyni Rust book.)

+0

Dzięki. Właśnie przeczytałem tę odpowiedź po kilku miesiącach. Częścią, która nie była oczywista, jest iterator leniwych map. Połączenie tego z funkcją przechwytywania przez odniesienie sprawia, że ​​wszystko ma sens. –