Iterator so với IntoIterator để làm phẳng

Oct 06 2020

Tôi đang chơi với các trình vòng lặp và tôi đã sử dụng Flattenđể hiểu rõ hơn về cách trình vòng lặp hoạt động. Ví dụ này:

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

Không biên dịch và đưa ra lỗi (cũng là lỗi chính cần quan tâm):

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

Điều làm tôi bối rối là có vẻ như Flattenyêu cầu lồng nhau Itemđể thực hiện IntoIterator. Tôi đã xác nhận điều này trong tài liệu gỉ, nơi họ tuyên bố

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

Ngay từ các tài liệu đã sẵn sàng, IntoIterator chỉ cung cấp một cách để xác định cách một kiểu được chuyển đổi thành một trình lặp. Tại sao đặc điểm không thể bị ràng buộc <I as Iterator>::Item: Iterator? Chắc chắn nếu mục lồng nhau triển khai Iterator, thì chúng ta cũng nhận được như vậy Flattenvì bản thân mục lồng nhau phải là các trình vòng lặp. Nó mang lại sự khác biệt / lợi ích gì khi sử dụng IntoIteratorthay thế?

Trả lời

1 twotwotwo Oct 06 2020 at 11:18

Yêu cầu IntoIteratorthay vì Iteratorcho phép bạn làm phẳng một bộ sưu tập ngoài một bộ sưu tập các trình vòng lặp.

Ví dụ Vec, kiểu tập hợp khác với kiểu trình lặp, vì vậy nếu được flattenyêu cầu các mục là Iterators, bạn không thể san bằng a Vectrong Vecs.

IntoIteratorđại diện cho một kiểu có thể được chuyển đổi thành một trình vòng lặp, chẳng hạn như một Vechoặc nhiều tập hợp khác (hoặc một trình vòng lặp!). Vì vậy, điều này, ví dụ, hoạt động:

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

Bạn có thể thử ví dụ này trên Playground . Sử dụng into_iter()ở trên thay vì iter()đảm bảo rằng chúng ta lặp qua i32s, không tham chiếu đến i32s. (Cảm ơn Cerberus đã đề xuất into_iter()thay vì copied().)

(Bạn có thể xem thêm một số kiểu có IntoIteratortriển khai trong phần "Triển khai về Loại nước ngoài" của tài liệu .)