如何在Rust中定义一个迭代器,用于遍历包含可迭代项的结构体?

4
如何在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包含两个向量,我想要一个迭代器,可以随机地在这两个元素之间来回跳跃。当然,这里有很多错误,但在核心上,我不知道如何创建一个结构体,它可以携带foofar这些项的迭代器,因为Rust将迭代器定义为一个特性而不是一种类型。
2个回答

6

在某个时刻,你必须定义Iterator将产生哪些Item,例如Iterator<Item = &'a f64>。为简化起见,我们将其转换为Iterator<Item = f64>,因为f64Copy类型,如果不需要引用,通常最好避免使用。

因此,然后我们会有编译错误:

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(),
    }
}

我们完成了基础部分,下一个问题是您的代码在next()中没有生成Bar类型,因此我们必须更正您的代码,并创建一个适当的随机生成器会更好。这里是最终的片段:

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

0

@Stargateur在很大程度上帮我解决了问题,但我想要包含两个独立的代码来完成任务。以下是修复后的代码,更接近于我的原始尝试,在Rust 1.34.1上运行正常:

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

// Iterator for the structure
struct FooIter<FloatIter, IntIter>
where
    FloatIter: Iterator<Item = f64>,
    IntIter: Iterator<Item = i64>,
{
    foo: std::iter::Peekable<FloatIter>,
    far: std::iter::Peekable<IntIter>,
}

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

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

// Implementation of the iterator
impl<FloatIter, IntIter> Iterator for FooIter<FloatIter, IntIter>
where
    FloatIter: Iterator<Item = f64>,
    IntIter: Iterator<Item = i64>,
{
    type Item = Bar;

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

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

帮助我理解发生了什么的是,FooIter 在泛型类型上参数化其参数。这些类型是通过在Fooiter方法中使用返回位置的impl Trait来推断的。话虽如此,我能够编写类似的代码而不使用这种推断:

extern crate rand;

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

// Iterator for the structure
struct FooIter<'a> {
    foo: std::iter::Peekable<std::slice::Iter<'a, f64>>,
    far: std::iter::Peekable<std::slice::Iter<'a, i64>>,
}

// Method that provides the iterator for use
impl Foo {
    fn iter<'a>(&'a self) -> FooIter<'a> {
        FooIter {
            foo: self.foo.iter().peekable(),
            far: self.far.iter().peekable(),
        }
    }
}

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

// Implementation of the iterator
impl<'a> Iterator for FooIter<'a> {
    type Item = Bar;

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

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

这几乎肯定不是正确的做法,但我想看看是否可能。我通过编译代码来确定迭代器类型:

fn main() {
    let x = vec![1.2, 2.3, 3.4];
    let y: i32 = x.iter().peekable();
}

这导致编译器出现错误:

error[E0308]: mismatched types
 --> junk.rs:4:19
  |
4 |     let y: i32 = x.iter().peekable();
  |                  ^^^^^^^^^^^^^^^^^^^ expected i32, found struct `std::iter::Peekable`
  |
  = note: expected type `i32`
             found type `std::iter::Peekable<std::slice::Iter<'_, {float}>>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.

这包含了我正在寻找的类型。再次强调,这几乎肯定不是正确的做法,但它帮助我理解了提供的答案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接