Iterator vs IntoIterator para nivelar

Oct 06 2020

Estou brincando com iteradores e tenho usado Flattenpara entender melhor como funcionam os iteradores. Este exemplo:

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

Não compila e dá o erro (bem, o erro principal de preocupação):

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}`

O que me confunde é que parece que Flattenrequer o aninhado Itempara implementar IntoIterator. Eu confirmei isso nos rustdocs, onde eles declaram

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

Depois de preparar os documentos, o IntoIterator apenas fornece uma maneira de definir como um tipo é convertido em um iterador. Por que o traço não poderia ser <I as Iterator>::Item: Iterator? Certamente, se o item aninhado for implementado Iterator, obteremos o mesmo, Flattenpois os próprios itens aninhados devem ser iteradores. Que diferença / benefício traz para uso em IntoIteratorvez disso?

Respostas

1 twotwotwo Oct 06 2020 at 11:18

Exigir, em IntoIteratorvez de Iteratorpermitir, simplificar uma coleção de coleções, além de uma coleção de iteradores.

O tipo de coleção, por exemplo Vec, é diferente do tipo de iterador, portanto, se flattennecessário que os itens sejam Iterators, você não poderá nivelar um Vecde Vecs.

IntoIteratorrepresenta um tipo que pode ser convertido em um iterador, como uma Vecou muitas outras coleções (ou um iterador!). Então isso, por exemplo, funciona:

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);
}

Você pode tentar este exemplo no Playground . Usar into_iter()acima em vez de iter()garante que iteremos sobre i32s, não referências a i32s. (Obrigado Cerberus por sugerir em into_iter()vez de copied().)

(Você pode ver mais alguns tipos com IntoIteratorimplementações na seção "Implementações em tipos estrangeiros" dos documentos .)