【问题标题】:How to define an iterator in Rust over a struct that contains items that are iterable?如何在包含可迭代项的结构上在 Rust 中定义迭代器?
【发布时间】:2019-06-02 06:56:31
【问题描述】:

如何在 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 将迭代器定义为特征而不是类型。

【问题讨论】:

    标签: rust


    【解决方案1】:

    您必须在某些时候定义Item Iterator 将产生什么,例如Iterator&lt;Item = &amp;'a f64&gt;。让我们简化并转换为Iterator&lt;Item = f64&gt;,因为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的真实类型可能很长而且不清楚,编译器会推断适合我们的类型。此外,我们必须将类型绑定到 &amp;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(),
        }
    }
    

    到这里我们完成了基础,下一个问题是你的代码没有产生Bar 输入next(),所以我们必须更正你的代码,并且创建一个适当的随机生成器会很好。所以这里是最终的sn-p:

    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&lt;Iterator&gt;,那么就这样做:

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

    【讨论】:

      【解决方案2】:

      @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`.
      

      这包含我正在寻找的类型。同样,这几乎肯定是错误的做法,但它帮助我理解了提供的答案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-09-17
        • 2021-11-14
        • 2021-11-24
        • 1970-01-01
        • 2019-01-10
        • 1970-01-01
        • 2014-08-07
        • 2016-12-25
        相关资源
        最近更新 更多