Почему я могу перебирать фрагмент дважды, но не вектор?

Если я попытаюсь дважды перебрать фрагмент, он будет работать нормально:

let a = &[1, 2, 3];
for i in a {
    println!("{}", i);
}
for i in a {            // works fine
    println!("{}", i);
}

Если я попытаюсь выполнить итерацию по вектору дважды, произойдет сбой:

let a = vec![1, 2, 3];
for i in a {
    println!("{}", i);
}
for i in a {
    println!("{}", i);
}
error[E0382]: use of moved value: 'a'
 --> src/main.rs:6:14
  |
3 |     for i in a {
  |              - value moved here
...
6 |     for i in a {
  |              ^ value used here after move
  |
  = note: move occurs because 'a' has type 'std::vec::Vec<i32>', which does not implement the 'Copy' trait

Я вижу, что черта IntoIterator принимает self по значению, поэтому для меня имеет смысл, что второй пример не удался. Почему первый пример успешен?

Ответ 1

Как вы сказали, for работ, взяв вещь, которую вы попросили IntoIterator::into_iter, и передав ее через IntoIterator::into_iter для получения фактического значения итератора. Также, как вы сказали, into_iter получает тему по значению.

Таким образом, при попытке перебрать Vector непосредственно, это означает, что вы пройдете весь вектор, по значению, в свою IntoIterator реализации, тем самым потребляя вектор в этом процессе. Вот почему вы не можете выполнить итерацию по вектору напрямую дважды: итерацию по нему в первый раз, когда он потребляется, после чего он больше не существует.

Однако срезы отличаются: срез является неизменным, заимствованным указателем на свои данные; неизменные, заимствованные указатели могут быть скопированы свободно. Это означает, что IntoIterator для неизменяемых фрагментов просто заимствует данные и не использует их (не так, как мог бы). Или, чтобы взглянуть на это иначе, его реализация IntoIterator просто берет копию среза, тогда как вы не можете скопировать Vec.

Следует отметить, что вы можете перебирать Vec не потребляя его, перебирая заем. Если вы проверите документацию для Vec, вы заметите, что в ней перечислены реализации IntoIterator для Vec<T>, &Vec<T> и &mut Vec<T>.

let mut a: Vec<i32> = vec![1, 2, 3];

for i in &a {           // iterate immutably
    let i: &i32 = i;    // elements are immutable pointers
    println!("{}", i);
}

for i in &mut a {       // iterate mutably
    let i: &mut i32 = i;// elements are mutable pointers
    *i *= 2;
}

for i in a {            // iterate by-value
    let i: i32 = i;     // elements are values
    println!("{}", i);
}

// 'a' no longer exists; it was consumed by the previous loop.