Как реализовать непотребляющий IntoIterator для структуры с базовой коллекцией? - PullRequest
0 голосов
/ 17 января 2020

Допустим, у меня есть struct, у которого есть коллекция, например Vec в качестве одного из элементов данных:

struct MyCollection {
    data: Vec<i32>
}

Я хочу, чтобы пользователь MyCollection мог перебирать свои данные без прямого доступа к самой Vec, например так:

let x = MyCollection{data:vec![1, 2, 3, 4, 5]};

for i in &x {
    //...
}

Однако я пытаюсь реализовать необходимую черту IntoIterator для не потребляющей версии с &x , Я успешно реализовал потребляющую версию:

impl std::iter::IntoIterator for MyCollection {
    type Item = i32;
    type IntoIter = std::vec::IntoIter<Self::Item>;

    fn into_iter(self) -> Self::IntoIter {
        return self.data.into_iter();
    }
}

Однако это можно использовать только следующим образом:

for i in x {
    println!("{:?}", i);
}

, которая потребляет x. Клонирование данных возможно, но довольно дорого, поэтому я бы хотел этого избежать.

Вот то, что я имею до сих пор для не потребляющей версии, которую я основал на реализации source из std::vec::Vec:

impl<'a> std::iter::IntoIterator for &'a MyCollection {
    type Item = &'a i32;
    type IntoIter = std::vec::IntoIter<Self::Item>;

    fn into_iter(self) -> Self::IntoIter {
        return self.data.into_iter();
    }
}

, что приводит к следующей ошибке компиляции:

error: mismatched types
error: expected &i32, found i32
note: expected type `std::vec::IntoIter<&i32>`
   found type `std::vec::IntoIter<i32>`
error: expected `std::vec::IntoIter<&i32>` because of return type

Я также пытался удалить &'a из type Item, так как в моем случае элементы data могут Copy, но это дает следующее:

error: cannot move out of `self.data` which is behind a shared reference
error: move occurs because `self.data` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait

Я понимаю, что функция хочет IntoIter вектора для ссылок, но я не уверен, как дайте это эффективно. Я новичок в Rust, поэтому я был бы очень признателен за ясность по поводу проблемы. Бонусные баллы, если вы также можете сказать мне, как создать изменяемый итератор для доступа на запись таким же образом.

1 Ответ

2 голосов
/ 17 января 2020

Во-первых, вы должны использовать тип слайса, ваш пользователь не должен знать, что ваш внутренний тип - вектор. Тогда ваша проблема заключается в том, что вы не должны использовать IntoIter тип, но Iter тип напрямую.

Простой пример:

struct MyCollection {
    data: Vec<i32>,
}

impl<'a> std::iter::IntoIterator for &'a MyCollection {
    type Item = <std::slice::Iter<'a, i32> as Iterator>::Item;
    type IntoIter = std::slice::Iter<'a, i32>;

    fn into_iter(self) -> Self::IntoIter {
        self.data.as_slice().into_iter()
    }
}

fn main() {
    let x = MyCollection {
        data: vec![1, 2, 3, 4, 5],
    };

    for i in &x {
        println!("{}", i);
    }
}
...