Как скопировать вместо заимствования i64 в замыкание в Rust?

У меня есть следующий минимальный пример моего кода:

fn main()
{
    let names : Vec<Vec<String>> = vec![
        vec!["Foo1".to_string(), "Foo2".to_string()],
        vec!["Bar1".to_string(), "Bar2".to_string()]
    ];
    let ids : Vec<i64> = vec![10, 20];

    names.iter().enumerate().flat_map(|(i,v)| {
        let id : i64 = ids[i];
        v.iter().map(|n| 
            (n.clone(), id)
        )
    });
}

Теперь, когда я собираю это с rustc Я получаю следующее сообщение об ошибке:

error[E0597]: `id` does not live long enough
  --> main.rs:12:16
   |
11 |         v.iter().map(|n| 
   |                      --- capture occurs here
12 |             (n.clone(), id)
   |                         ^^ borrowed value does not live long enough
13 |         )
14 |     });
   |     -- borrowed value needs to live until here
   |     |
   |     borrowed value only lives until here

Но в моем понимании id имеет тип i64 и поэтому должен быть в состоянии быть скопирован в захват, будет ли именно то, что мне нужно?

Я также пытался включить id переменная, но безрезультатно:

error[E0597]: `i` does not live long enough
  --> main.rs:11:21
   |
10 |             v.iter().map(|n| 
   |                          --- capture occurs here
11 |                 (n.clone(), ids[i])
   |                                 ^ borrowed value does not live long enough
12 |             )
13 |         });
   |         -- borrowed value needs to live until here
   |         |
   |         borrowed value only lives until here

Так как же я могу скопировать свое целое число в замыкание вместо того, чтобы заимствовать его?

Я пытался с помощью move, но rustc это тоже не нравится

error[E0507]: cannot move out of captured outer variable in an `FnMut` closure
  --> main.rs:10:17
   |
7  |         let ids : Vec<i64> = vec![10, 20];
   |             --- captured outer variable
...
10 |             v.iter().map(move |n| 
   |                          ^^^^^^^^ cannot move out of captured outer variable in an `FnMut` closure

Так что мне как-то нужно получить rustc перемещать / копировать только некоторые, но не другую переменную?

2 ответа

Решение

Когда вы создаете замыкание в Rust, оно захватывает переменные либо по значению, либо по ссылке. Сочетание обоих невозможно. По умолчанию он захватывает по ссылке, но с move ключевое слово, оно захватывает по значению (то есть перемещает захваченные переменные внутри замыкания).

Итак, в вашем первом коде, вам нужно переместить id внутри закрытия:

fn main() {
    let names: Vec<Vec<String>> = vec![
        vec!["Foo1".to_string(), "Foo2".to_string()],
        vec!["Bar1".to_string(), "Bar2".to_string()],
    ];
    let ids: Vec<i64> = vec![10, 20];

    names.iter().enumerate().flat_map(|(i, v)| {
        let id: i64 = ids[i];
        v.iter().map(move |n| (n.clone(), id))
    });
}

Затем вы спрашиваете, можете ли вы "встроить" ids:

fn main() {
    let names: Vec<Vec<String>> = vec![
        vec!["Foo1".to_string(), "Foo2".to_string()],
        vec!["Bar1".to_string(), "Bar2".to_string()],
    ];
    let ids: Vec<i64> = vec![10, 20];

    names.iter().enumerate().flat_map(|(i, v)| {
        v.iter().map(|n| (n.clone(), ids[i]))
    });
}

Вы не можете поставить ids вообще в вашей внутренней замкнутости, потому что вы уже внутри FnMut закрытие (это требует эксклюзивного доступа). Таким образом, вы не можете одолжить или переместить ids потому что он уже заимствован FnMut закрытие. Минимальное воспроизведение:

fn main() {
    let mut i = 0;

    let mut closure = || {
        i = 2;
        || {
            println!("i = {}", i);
        }
    };

    closure()();
}

Вы можете переместить переменную в закрытие с помощью move ключевое слово. Здесь вам нужно изменить замыкание как:

v.iter().map(move |n|  // move is the keyword for moving variables into closure scope.
    (n.clone(), id)
)

Детская площадка

Другие вопросы по тегам