首页 > 解决方案 > 如何在包含可迭代项的结构上在 Rust 中定义迭代器?

问题描述

在 Rust 中,如何在包含已经可迭代的项目的结构上定义迭代器?这是迭代器的一次尝试

use rand;

// Structure of items
struct Foo {
    foo: Vec<f64>,
    bar: Vec<i64>,
}

// Iterator for the structure
struct FooIter {
    foo: Iterator,
    bar: Iterator,
}

// Method that provides the iterator for use
impl Foo {
    fn iter(&self) -> FooIter {
        FooIter {
            foo: self.foo.iter().peek(),
            bar: self.bar.iter().peek(),
        }
    }
}

// Item desired from iterator
enum Bar {
    MyFloat(f64),
    MyInt(i64),
}

// Implementation of the iterator
impl Iterator for FooIter {
    type Item = Bar;

    fn next(&mut self) -> Option<Bar> {
        match (self.foo.peek(), self.far.peek()) {
            (Some(_), Some(_)) => {
                if rand::random() {
                    self.foo.next()
                } else {
                    self.bar.next()
                }
            }
            (Some(_), None) => self.foo.next(),
            (None, Some(_)) => self.bar.next(),
            (None, None) => None,
        }
    }
}

// Iterate over a struct
fn main() {
    let fuz = Foo {
        foo: vec![1.2, 2.3, 3.4],
        bar: vec![5, 6],
    };
    for item in fuz.iter() {
        match item {
            Bar::MyFloat(f) => println!("float : {}", f),
            Bar::MyInt(i) => println!("int : {}", i),
        }
    }
}

简而言之,该结构Foo包含两个向量,我想要一个在两个元素之间来回跳转的迭代器。当然,这里有很多错误,但核心是,我不明白如何创建一个包含项目迭代器的结构,foo因为farRust 将迭代器定义为特征而不是类型。

标签: rust

解决方案


您必须在某些时候定义将产生什么ItemIterator例如Iterator<Item = &'a f64>。让我们简化并转换为Iterator<Item = f64>,因为f64如果Copy不需要它,最好避免引用。

因此,我们将有编译错误:

error[E0277]: the size for values of type `(dyn std::iter::Iterator<Item = f64> + 'static)` cannot be known at compilation time
  --> src/main.rs:11:5
   |
11 |     foo: std::iter::Iterator<Item = f64>,
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `std::marker::Sized` is not implemented for `(dyn std::iter::Iterator<Item = f64> + 'static)`
   = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
   = note: only the last field of a struct may have a dynamically sized type

为了避免动态类型并同时修复错误,让我们定义一些泛型类型:

// Iterator for the structure
struct FooIter<F, I> {
    foo: F,
    bar: I,
}

我们在我们的实现中添加了必要的Iterator

impl<F, I> Iterator for FooIter<F, I>
where
    F: Iterator<Item = f64>,
    I: Iterator<Item = i64>,

而且我们必须改变我们的生成方式FooIter,这次我们将使用一个神奇的关键字impl,这样可以避免编写Iterator可能很长且不清楚的真实类型,编译器会为我们推断类型。此外,我们必须将类型绑定到的生命周期,&self因为只要迭代器存在,它就必须被借用,只需声明'a生命周期并添加即可+ 'a

fn iter<'a>(
    &'a self,
) -> FooIter<impl Iterator<Item = f64> + 'a, impl Iterator<Item = i64> + 'a> {
    FooIter {
        foo: self.foo.iter().copied(),
        bar: self.bar.iter().copied(),
    }
}

到这里我们完成了基础,下一个问题是你的代码没有产生Bartype in next(),所以我们必须更正你的代码,而且创建一个适当的随机生成器会很好。所以这里是最后的片段:

use rand::{rngs::ThreadRng, thread_rng, Rng};

// Structure of items
struct Foo {
    foo: Vec<f64>,
    bar: Vec<i64>,
}

// Iterator for the structure
struct FooIter<'r, F, I> {
    foo: F,
    bar: I,
    rng: &'r mut ThreadRng,
}

// Method that provides the iterator for use
impl Foo {
    fn iter<'a, 'r: 'a>(
        &'a self,
        rng: &'r mut ThreadRng,
    ) -> FooIter<impl Iterator<Item = f64> + 'a, impl Iterator<Item = i64> + 'a> {
        FooIter {
            foo: self.foo.iter().copied(), // nigthly feature, use cloned() for stable
            bar: self.bar.iter().copied(),
            rng,
        }
    }
}

// Item desired from iterator
enum Bar {
    MyFloat(f64),
    MyInt(i64),
}

// Implementation of the iterator
impl<'r, F, I> Iterator for FooIter<'r, F, I>
where
    F: Iterator<Item = f64>,
    I: Iterator<Item = i64>,
{
    type Item = Bar;

    fn next(&mut self) -> Option<Bar> {
        if self.rng.gen() {
            self.foo
                .next()
                .map(|x| Bar::MyFloat(x))
                .or_else(|| self.bar.next().map(|x| Bar::MyInt(x)))
        } else {
            self.bar
                .next()
                .map(|x| Bar::MyInt(x))
                .or_else(|| self.foo.next().map(|x| Bar::MyFloat(x)))
        }
    }
}

// Iterate over a struct
fn main() {
    let fuz = Foo {
        foo: vec![1.2, 2.3, 3.4],
        bar: vec![5, 6],
    };
    for item in fuz.iter(&mut thread_rng()) {
        match item {
            Bar::MyFloat(f) => println!("float : {}", f),
            Bar::MyInt(i) => println!("int : {}", i),
        }
    }
}

请注意,如果您仍然想要,Peekable<Iterator>那么只需执行以下操作:

struct FooIter<'r, F, I>
where
    F: Iterator<Item = f64>,
    I: Iterator<Item = i64>,
{
    foo: Peekable<F>,
    bar: Peekable<I>,
    rng: &'r mut ThreadRng,
}

// Method that provides the iterator for use
impl Foo {
    fn iter<'a, 'r: 'a>(
        &'a self,
        rng: &'r mut ThreadRng,
    ) -> FooIter<impl Iterator<Item = f64> + 'a, impl Iterator<Item = i64> + 'a> {
        FooIter {
            foo: self.foo.iter().copied().peekable(),
            bar: self.bar.iter().copied().peekable(),
            rng,
        }
    }
}

推荐阅读