预期的std :: iter :: Iterator,但找到了std :: iter :: Iterator

时间:2020-01-01 10:13:01

标签: rust

我想表达以下内容:

给定一个矩阵和两个索引增量,返回矩阵中所有数字的四联体:沿行,列或对角线的四联体。

use std::iter::Iterator;
use std::iter::Peekable;
use std::ops::Range;

struct Quads<'a> {
   mx: &'a Vec<Vec<u32>>,
   xs: &'a mut Peekable<Range<i32>>,
   ys: &'a mut Peekable<Range<i32>>,
   dx: i32,
   dy: i32,
}

impl<'a> Quads<'a> {
   fn new(mx: &'a Vec<Vec<u32>>, dx: i32, dy: i32) -> Quads<'a> {
      let ys = (if dy < 0 { -3 * dy } else { 0 })..(mx.len() as i32 - if dy > 0 { 4 * dy } else { 0 });
      let xs = 0..0;

      Quads{
         mx: mx,
         xs: &mut xs.peekable(),
         ys: &mut ys.peekable(),
         dx: dx,
         dy: dy,
      }
   }
}

impl<'a> Iterator for Quads<'a> {
   type Item = &'a mut dyn Iterator<Item = u32>;

   fn next(&mut self) -> Option<Self::Item> {
      while self.xs.peek() == None && self.ys.peek() != None {
         self.xs = &mut ((if self.dx < 0 { -3 * self.dx } else { 0 })..
                         (self.mx[0].len() as i32 - if self.dx > 0 { 4 * self.dx } else { 0 }))
                         .peekable();
         self.ys.next();
      }

      let y = self.ys.peek();
      if y == None {
         return None;
      }

      let y = *y.unwrap();
      let x = self.xs.next().unwrap();

      Some(&mut ((x..).step_by(self.dx as usize)
                      .zip((y..).step_by(self.dy as usize))
                     .take(4)
                     .map(|(x,y)| self.mx[y as usize][x as usize])))
   }
}

这会产生令人困惑的错误消息:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/main.rs:52:27
   |
52 |                      .map(|(x,y)| self.mx[y as usize][x as usize])))
   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 33:4...
  --> src/main.rs:33:4
   |
33 | /    fn next(&mut self) -> Option<Self::Item> {
34 | |       while self.xs.peek() == None && self.ys.peek() != None {
35 | |          self.xs = &mut ((if self.dx < 0 { -3 * self.dx } else { 0 })..
36 | |                          (self.mx[0].len() as i32 - if self.dx > 0 { 4 * self.dx } else { 0 }))
...  |
52 | |                      .map(|(x,y)| self.mx[y as usize][x as usize])))
53 | |    }
   | |____^
   = note: ...so that the types are compatible:
           expected &&mut Quads<'a>
              found &&mut Quads<'a>
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 30:6...
  --> src/main.rs:30:6
   |
30 | impl<'a> Iterator for Quads<'a> {
   |      ^^
   = note: ...so that the types are compatible:
           expected std::iter::Iterator
              found std::iter::Iterator

似乎表明它找到了与所寻找的相同的东西。那怎么了?


预期用途

https://projecteuler.net/problem=11

当然,可以用更直接的方法解决该问题,但是我正在学习如何在Rust中表达复杂的事物。因此,在这里,我试图表示一个Quad,它是一个Iterator,可以从那个欧拉问题中提取四倍的数,其中每个四倍数本身就是一个Iterator

Quad内的所有内容都代表Iterator的状态。 xsys代表从其开始下一个四元组的“当前单元格”的坐标的迭代器。 next然后尝试查看是否到达行尾,并通过将xs重新初始化为新的Iterator前进到下一行。当ys到达最后一行之后,我们提取了所有的四倍。

然后这样:

for q in Quad::new(mx, 1, 0) {  ... process all quadruples along the rows }
for q in Quad::new(mx, 0, 1) {  ... process all quadruples along the columns }
for q in Quad::new(mx, 1, 1) {  ... process all quadruples along one diagonal }
for q in Quad::new(mx, 1, -1) {  ... process all quadruples along the other diagonal }

我想我已经抓住了这个主意,但是我不知道编译器对此不满意,因此不知道如何前进。

2 个答案:

答案 0 :(得分:2)

好,所以我知道了。 rustc产生如此令人困惑的错误消息真的没有帮助-显然,它找到了要查找的内容,但仍然不满意。

所发布的代码存在一些问题。我最初的假设是,通过将引用标记为可变的,我可以告诉编译器,从此以后接收引用的任何人都应对它负责,包括内存管理。尽管在某些情况下可能是正确的(我不确定;尚需弄清楚),但它肯定不适用于struct字段(Quad的{​​{1}})并返回值。在这种情况下,我们可以在xs&mut的声明中摆脱xs

ys

另一个问题是,如果它不是引用,则如何限制值的生存期。 (例如,在这种情况下,struct Quads<'a> { mx: &'a Vec<Vec<u32>>, xs: Peekable<Range<i32>>, ys: Peekable<Range<i32>>, dx: i32, dy: i32, } 返回的Iterator仅在next内有效)

另一个问题是表达式问题:如何使mx返回next(我不想泄漏哪种Iterator),以便编译器快乐。 (例如,Iterator不会执行-“在编译时不知道大小”)。可以通过使用dyn Iterator来解决这两个问题,该问题也可以使用生命周期进行注释:

Box

另一个问题是,即使impl<'a> Iterator for Quads<'a> { type Item = Box<dyn Iterator<Item = u32> + 'a>; fn next(&mut self) -> Option<Self::Item> { ... } } 的使用是只读的,闭包mx也会捕获|(x, y)| self.mx[y][x],这是一个可变的引用。这很简单:先获取一个局部变量,然后再输入self

move

几乎忘了。而真正奇怪的是,即使我最初键入它时也看起来很可疑: let mx = self.mx; Some(Box::new(... .map(move |(x, y)| mx[y as usize][x as usize]))) 取了step_by,它是无符号的,并且实际上并没有通过添加一个{给定增量;而是构造一个usize来跳过给定数量的元素(几乎所有元素)。因此,需要一个元组迭代器:

Range

因此,您无需使用Iterator来压缩两个struct Tup<T> { x: (T, T), d: (T, T), } ... impl<T: AddAssign + Copy> Iterator for Tup<T> { type Item = (T, T); fn next(&mut self) -> Option<(T, T)> { self.x.0 += self.d.0; self.x.1 += self.d.1; Some(self.x) } } ,而是使用已知的初始值和增量初始化了Iterator

step_by

答案 1 :(得分:1)

另一种解决方案不是导出迭代器,而是传递操作以在内部运行。