Итератор против IntoIterator для сглаживания

Oct 06 2020

Я играю с итераторами и использую их, Flattenчтобы лучше понять, как работают итераторы. Этот пример:

assert_eq!(vec![1, 2].iter().flatten().count(), 2);

Не компилируется и выдает ошибку (ну, это основная ошибка, вызывающая беспокойство):

error[E0277]: `&{integer}` is not an iterator
  --> src/lib.rs:59:38
   |
59 |         assert_eq!(vec![1, 2].iter().flatten().count(), 2);
   |                                      ^^^^^^^ `&{integer}` is not an iterator
   |
   = help: the trait `std::iter::Iterator` is not implemented for `&{integer}`
   = note: required because of the requirements on the impl of `std::iter::IntoIterator` for `&{integer}`

Что меня смущает, так это то, что, похоже, для реализации Flattenтребуется вложенное . Я подтвердил это в rustdocs, где они заявляютItemIntoIterator

pub struct Flatten<I> 
where
    I: Iterator,
    <I as Iterator>::Item: IntoIterator

На момент подготовки документации IntoIterator просто предоставляет способ определить, как тип преобразуется в итератор. Почему нельзя было привязать черту <I as Iterator>::Item: Iterator? Конечно, если вложенный элемент реализует Iterator, то мы получим то же самое, Flattenпоскольку вложенные элементы сами должны быть итераторами. Какую разницу / пользу он приносит IntoIteratorвместо этого?

Ответы

1 twotwotwo Oct 06 2020 at 11:18

Требование, IntoIteratorа не Iteratorпозволяет сгладить коллекцию коллекций в дополнение к коллекции итераторов.

Тип коллекции, например Vec, отличается от типа итератора, поэтому, если flattenтребуется, чтобы элементы были Iterators, вы не могли бы сгладить a Vecof Vecs.

IntoIteratorпредставляет тип, который можно преобразовать в итератор, такой как Vecколлекция или многие другие (или итератор!). Так, например, работает:

fn main() {
    let nested_vec: Vec<Vec<i32>> = vec![vec![1, 2], vec![3, 4]];
    let flat_vec: Vec<i32> = nested_vec.into_iter().flatten().collect();
    println!("{:?}", flat_vec);
}

Вы можете попробовать этот пример на игровой площадке . Использование into_iter()выше вместо iter()гарантирует, что мы перебираем i32s, а не ссылки на i32s. (Спасибо Cerberus за предложение into_iter()вместо copied().)

(Вы можете увидеть еще несколько типов с IntoIteratorреализациями в разделе документации «Реализации для внешних типов» .)