【问题标题】:Expected std::iter::Iterator, but std::iter::Iterator found预期 std::iter::Iterator,但找到 std::iter::Iterator
【发布时间】:2020-04-20 10:12:27
【问题描述】:

我试图表达以下内容:

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

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 }

我想我已经抓住了这个想法,但我不知道编译器不喜欢它的什么地方,以及如何继续前进。

【问题讨论】:

  • 那个很奇怪的锈代码,我不太明白你想做什么。
  • @Stargateur 我已经添加了“预期用途”,如果有帮助的话。
  • @Stargateur 我想通了。想通过关于可变引用的内存管理的声明吗? (如:谁拥有可变引用对象的释放?)

标签: rust


【解决方案1】:

好的,所以我想通了。 rustc 产生如此令人困惑的错误消息真的没有帮助 - 显然,它找到了它正在寻找的东西,但仍然不满意。

发布的代码存在几个问题。我最初的假设是,通过将引用标记为可变的,我可以告诉编译器,无论谁收到该引用,都将对它负责,包括内存管理。虽然在某些情况下可能是真的(我不确定;这还有待弄清楚),但它肯定不适用于struct 字段(Quadxs)和返回值。在这种情况下,我们可以在xsys的声明中去掉&amp;mut

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

另一个问题是,如果它不是一个引用,你如何限制一个值的生命周期。 (例如,在这种情况下,next 返回的Iterator 仅在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)
   }
}

因此,不是用step_by 压缩两个Iterators,而是使用已知初始值和增量初始化Tup

  Some(Box::new(Tup::new((x, y), (self.dx, self.dy))
                     .take(4)
                     .map(move |(x, y)| mx[y as usize][x as usize])))

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 绝对是一个选择。但这也取决于很多事情。特别是,Fn 确实是一个对象,它需要复制到其中的所有内容。它也可能与生命周期和可变性发生冲突。因此,您需要预测预期用途。例如,我必须在这里声明FnMut,因为我想传递给它的闭包正在改变一些状态:fn collatz_seq(mut x: u64, mut f: impl FnMut(u64)-&gt;bool)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多