#rust #iterator #traits
Вопрос:
Я хочу реализовать Iterator
признак для структуры, которая содержит повторяющееся поле. Повторение моей структуры должно привести к тем же результатам, что и при повторении по полю. Это то, что я хотел бы иметь (очевидно, не работает):
struct Foo {
bar: Vec<char>,
}
impl Iterator for Foo {
type Item: amp;char; // Error: expected named lifetime parameter
fn next(amp;mut self) -> Option<Self::Item> {
self.bar.iter().next()
}
}
Чтобы избежать этой ошибки, я попытался вставить время жизни:
use std::marker::PhantomData;
struct Foo<'a> {
bar: Vec<char>,
phantom: amp;'a PhantomData<char> // not sure what to put inside < .. >
}
impl<'a> Iterator for Foo<'a> {
type Item = amp;'a char;
fn next(amp;mut self) -> Option<Self::Item> {
self.bar.iter().next() // again, several errors about lifetimes
}
}
Как мне реализовать Iterator
признак для такой структуры?
Комментарии:
1. Если возможно, вы можете
Deref
вместо этого захотеть реализовать. Таким образом, вы можете предоставить методы поля (в том числе.iter()
) без написания оболочек для вашей структуры.
Ответ №1:
Существует большая разница между тем, что создает ан Iterator
, и тем, что является ан Iterator
. Например, Vec<char>
может создать итератор, но сам по себе не является итератором. Вот несколько простых примеров того и другого, так что вы, надеюсь, сможете найти что-то подходящее для вашего варианта использования.
Создание итератора
Самый простой подход для вашей ситуации-просто реализовать Deref
его в полевых условиях и позволить Vec<char>
ему справиться с этим. В качестве альтернативы вы можете написать функцию-оболочку для bar.iter()
.
pub struct Foo {
bar: Vec<char>,
}
impl Deref for Foo {
type Target = Vec<char>;
fn deref(amp;self) -> amp;Self::Target {
amp;self.bar
}
}
let foo = Foo { bar: vec!['a', 'b', 'c', 'd'] };
// deref is implicitly called so foo.iter() represents foo.bar.iter()
for x in foo.iter() {
println!("{:?}", x);
}
Написание итератора
Вот как вы можете написать свой собственный итератор для a Vec<char>
. Обратите внимание, как Vec
данные хранятся в качестве ссылки, а не в качестве собственного значения. Это делается для того, чтобы ржавчина могла устранить ограничения на срок службы. Удерживая неизменяемую ссылку в течение всего срока службы итератора, мы гарантируем, что ссылки, созданные этим итератором, также могут сохраняться в течение этого срока службы. Если мы использовали собственное значение, мы можем гарантировать только то, что срок службы ссылки на элемент продлится до следующего раза, когда изменяемая ссылка будет передана итератору. Или, другими словами, каждое значение может сохраняться только до тех пор, пока next
не будет вызвано снова. Однако даже для этого требуются ночные функции для правильного выражения.
pub struct SimpleIter<'a> {
values: amp;'a Vec<char>,
index: usize,
}
impl<'a> Iterator for SimpleIter<'a> {
type Item = amp;'a char;
fn next(amp;mut self) -> Option<Self::Item> {
if self.index >= self.values.len() {
return None
}
self.index = 1;
Some(amp;self.values[self.index - 1])
}
}
Вот простой пример универсального итератора, обертывающего другой итератор.
// Add ?Sized so Foo can hold a dynamically sized type to satisfy IntoFoo
struct Foo<I: ?Sized> {
bar: I,
}
impl<I: Iterator> Iterator for Foo<I> {
type Item = <I as Iterator>::Item;
fn next(amp;mut self) -> Option<Self::Item> {
println!("Iterating through Foo");
self.bar.next()
}
}
Вы также можете получить немного больше фантазии с его помощью, сделав черту для легкого использования Foo
.
pub trait IntoFoo {
fn iter_foo(self) -> Foo<Self>;
}
// Add an iter_foo() method for all existing iterators
impl<T: Iterator> IntoFoo for T {
fn iter_foo(self) -> Foo<Self> {
Foo { bar: self }
}
}
let values = vec!['a', 'b', 'c', 'd'];
// Get default iterator and wrap it with our foo iterator
let foo: Foo<std::slice::Iter<'_, char>> = values.iter().iter_foo();
for x in foo {
println!("{:?}", x);
}
Ответ №2:
Итератор-это объект с отслеживанием состояния, который запоминает свое положение в базовой коллекции. В вашем примере каждый вызов next()
будет перезапускаться с самого начала и возвращать первый элемент (при условии, что ошибки были исправлены).
Реализация Iterator
for Foo
означала бы , что сам экземпляр будет изменяться при каждом вызове next()
, поэтому итераторы обычно имеют свою собственную структуру.
Если вы хотите предоставить возможность итерации, я бы посоветовал добавить iter()
функцию, которая может возвращать a std::slice::Iter
(которая Vec::iter()
возвращает).
struct Foo {
bar: Vec<char>,
}
impl Foo {
pub fn iter(amp;self) -> impl Iterator<Item=amp;char> '_ {
self.bar.iter()
}
}