我可以根据闭包指定迭代器的类型吗?

Can I specify the type for iterators based on closures?

我写了一个函数来遍历二维网格中单元格的邻居:

pub fn neighbours(
    (width, height): (usize, usize),
    (x, y): (usize, usize),
) -> impl Iterator<Item = (usize, usize)> {
    [(-1, 0), (1, 0), (0, -1), (0, 1)]
        .iter()
        .map(move |(dx, dy)| (x as i64 + dx, y as i64 + dy))
        .filter(move |&(nx, ny)| 0 <= ny && ny < height as i64 && 0 <= nx && nx < width as i64)
        .map(|(nx, ny)| (nx as usize, ny as usize))
}

请注意,它 returns 一个 impl Iterator<Item = (usize, usize)>

如果我理解正确,返回 impl 会导致代码变慢,调用函数指针而不是将事情编译成简单的循环。对吗?

所以想指定一个更精确的类型,我用 () 替换了类型,看看编译器推断出什么类型,它推断出

std::iter::Map<std::iter::Filter<std::iter::Map<std::slice::Iter<'l, (i64, i64)>, _>, _>, _>

其中 _s 代表闭包,我不知道如何指定它们的类型。

我试图将闭包提取到具有 Fn 特征的结构,但无法实现,而且 "

If I understand correctly, returning an impl would result in slower code, calling function pointers instead of compiling things down to simple loops. Right?

没有。返回 impl Iterator<Item = (usize, usize)> 在代码生成方面与 returning Map<Filter<Map<...>>> 完全相同。¹ 区别是双重的:

  • 更改 neighbours 的 return 类型不需要更改其签名,因此 impl Iterator 与其 return 类型的更改向前兼容。
  • impl Iterator 不会与其他不透明的 impl Iterator 类型统一,即使它们恰好具有相同的基础类型。 (简单来说:编译器不允许您从不同的来源创建 impl IteratorVec,即使所有这些不透明类型都是相同的具体类型。)

这些差异对代码生成或编译器内联任何内容的能力都没有任何影响,所以继续使用 impl Iterator

有一种情况你仍然必须使用间接调度(dyn Iterator):当函数neighbours本身是特征的一部分时,impl Trait语法尚不可用(截至 1.59)。目前解决这个问题最好的办法就是returnBox<dyn Iterator>。 (但是请注意,这并不意味着 每个 调用都将被动态调度;对 .next() 的调用将被动态调度,但是“内部”的所有内容仍然使用易于优化的静态调度。)

相关问题

  • What is the correct way to return an Iterator (or any other trait)?
  • (当方法是特征的一部分时)

¹ 请注意,为了实际 return Map<Filter<Map<...>>>,您仍然必须使用 impl Trait 来表示闭包,因为他们有匿名类型。