【发布时间】:2022-01-23 16:13:25
【问题描述】:
我编写了一个函数来遍历二维网格中的相邻单元格:
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))
}
注意它返回一个impl Iterator<Item = (usize, usize)>。
如果我理解正确,返回 impl 会导致代码变慢,调用函数指针而不是将事情编译成简单的循环。对吧?
所以想指定一个更精确的类型,我把类型替换为(),看看编译器推断出什么类型,它会推断出什么
std::iter::Map<std::iter::Filter<std::iter::Map<std::slice::Iter<'l, (i64, i64)>, _>, _>, _>
_s 代表我不知道如何指定其类型的闭包。
我尝试将闭包提取到具有 Fn 特征的结构,但无法使其正常工作,而且 IIUC implementing Fn traits is an unstable feature and I shouldn't use it"
【问题讨论】:
-
impl只是语法糖iirc。它实际上将使用泛型并为您使用该函数的每种类型进行编译(这是您想要手动执行的操作)。但是让我们看看是否有人证实了这一点 -
来自the book:“impl Trait 语法让您可以简洁地指定函数返回实现 Iterator trait 的某种类型,而无需写出很长的类型。”。它不是动态的,例如你不能有一个分支并返回两种不同的类型。正如@Netwave 所说,当你因为太长或涉及闭包而无法编写类型时,它是一种工具
-
Iterator不是最合适的返回类型吗?我认为如果要导出完整的内部类型,它会泄露很多关于实现的信息,并且每次实现更改时都需要更改。我对这些构造的编译表示不够熟悉,但我会假设在这种情况下更改类型注释也不会更改生成的代码。 -
您可能将
impl(如上所述,只是泛型)与dyn混淆了。后者意味着返回一个"trait object",这将抹去具体类型的任何知识,并且确实会产生一些运行时成本,因为为了调用这些对象上的任何方法,必须首先查找vtable。 -
@Netwave 虽然
impl确实不是动态的,并且没有性能损失,但impl(在返回位置)只是不是语法糖。无法将返回impl Trait的函数在语法上转换为使用命名泛型的函数(这就是“语法糖”所暗示的)。引入impl Trait是为了支持期货的高效返回,这对于 async/await 至关重要,而当时的泛型无法实现这一点。