自定义类型上Range<T>的Rustimpl迭代器

我有一个点结构,像这样:

#[derive(Clone, Copy, Debug)]
struct Point {
  x: i32,
  y: i32,
}

我想像这样在点之间迭代:

let start = Point { x: 0, y: 0 };
let end = Point { x: 3, y: 3 };
for p in start..end {
  // p should be
  // (0, 0)
  // (1, 0)
  // (2, 0)
  // (0, 1)
  // (1, 1)
  // (2, 1)
  // (0, 2)
  // etc
}

游乐场。

但是,我似乎无法实现这一点。start..end产生 a Range<Pos>,它是标准库中的一个类型。所以我不能做一个 custom impl Iterator for Range<Pos>,因为这些类型都不在当前的板条箱中。

我也不能impl Step for Point,因为迭代器必须知道范围的开始和结束。Step不会向您提供此信息。

最后,我无法为 编写包装器Iterator,因为impl MyIterator for Range<Point>会失败并显示Step未为 实现的错误Point

有没有办法解决这个问题?我目前的解决方案是在 ato上定义一个函数Point,以便您可以这样写:

for p in start.to(end) {
  // works as expected
}

to函数生成一个自定义迭代器,它可以满足我的要求。但是,我仍然想使用..语法。

编辑:AFAIK,这是工作所需的。根据这些评论,这看起来不可能。

回答

正如您所发现的,您不能直接执行此操作。但是我不会太在意语法,因为point..point不一定会使代码更具可读性。事实上,它隐藏了有关排序的信息,并且使以后添加按列而不是行的迭代器变得更加困难。

传达顺序但仍使用范围语法的设计可能是:

fn row_major(range: Range<Point>) -> impl Iterator<Item = Point> {
    (range.start.y..range.end.y)
        .flat_map(move |y| (range.start.x..range.end.x).map(move |x| Point { x, y }))
}

fn main() {
    let start = Point { x: 0, y: 0 };
    let end = Point { x: 2, y: 2 };
    let result: Vec<_> = row_major(start..end).collect();

    assert_eq!(
        result,
        vec![
            Point { x: 0, y: 0 },
            Point { x: 1, y: 0 },
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
        ]
    )
}

row_major可能不是最好的名称,这取决于您的应用程序。其他选项包括horizontal_iter,hrangeiter_rows。给它起一个这样的名字,读者可以清楚地知道预期的顺序,并且还可以在以后添加函数的对称列主(垂直)版本。

您还可以将其扩展到支持..endstart..=end变体以获得更多表现力,同时排除start..此处没有意义的。


以上是自定义类型上Range&lt;T&gt;的Rustimpl迭代器的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>