【发布时间】:2019-01-12 00:37:15
【问题描述】:
是否有使用返回 Iterator<Item = &mut T> 的闭包的函数示例?
我想编写几个 Rust 函数,它们多次迭代集合的内容,并且可能向后迭代。 IntoIterator 单独是不够的,因为它按值使用其参数,防止多次迭代。迭代器可以经常克隆,但是可变引用的迭代器。
如果我们真的只需要对集合的确切元素进行迭代,那么我们可以将 &mut C: IntoIterator 用于所有 Rust 集合类型 C。接受RFC 2289 语法,这可能看起来像:
fn batch_normalization<II: ?Sized>(v: &mut II)
where
for<'a> &'a mut II: IntoIterator<Item = &'a mut Self, IntoIter: DoubleEndedIterator + ExactSizeIterator>,
但当前表单遇到compiler bug。此外,这将不允许用户使用迭代器适配器指定集合内容的“视图”,例如 map。
直观地说,我们应该使用在调用时重建迭代器的闭包来借用集合:
fn batch_normalization<F>(f: F)
where
F: FnMut() -> impl Iterator<Item = &mut Self> + DoubleEndedIterator + ExactSizeIterator
我们还不能这样写,因为 (a) 特征中围绕 impl Trait 的问题尚未解决,并且 (b) 我们的 &mut Self 需要生命周期,所以我们可以这样写:
fn batch_normalization<I, F: FnMut() -> I>(f: F)
where
I: Iterator<Item = BorrowMut<Self>> + DoubleEndedIterator + ExactSizeIterator
我已经尝试过各种类似的公式,但没有一个完全奏效,主要是因为Item 比迭代器寿命更长。
我们应该像&'a mut C: IntoIterator<Item = &'a mut T> 那样解决这个问题,在FnMut 中明确地将项目的生命周期与&mut self 的生命周期联系起来。在伪代码中:
fn batch_normalization<I, F: FnMut() -> I>(f: F)
where
I: for<'a: F::Output> Iterator<Item = &'a mut Self> + DoubleEndedIterator + ExactSizeIterator
应该如何从作为参数传递的闭包中实际返回Iterator<Item = &mut T>?是否应该总是使用一些fn 指针混乱而不是闭包?大致:
fn batch_normalization<'a, I, V: ?Sized>(v: &mut V, f: fn(&'a mut V) -> I)
where
I: Iterator<Item = &'a mut Self> + DoubleEndedIterator + ExactSizeIterator
{
for x in f() { }
// ...
for x in f().rev() { }
}
【问题讨论】:
-
迭代器通常可以廉价地克隆。像this 这样的东西适合你吗?此外:如果您可以将示例最小化一点,它可能会很有用。就像那里的
Self是什么?那很重要么?提醒一下:minimal reproducible example. -
我不明白这不允许用户使用迭代器适配器(如 map)指定集合内容的“视图”。无论如何,你不能在同一个
Map上迭代两次。听起来您可能正在寻找更多类似于基于光标的 API,而不是基于迭代器的 API。 -
关于克隆迭代器 Lukas 的观点非常好。我认为这可以解决大多数此类情况,谢谢!恐怕
std::slice::IterMut无法克隆但是:doc.rust-lang.org/std/slice/struct.IterMut.html -
是的,基于游标的 API 肯定会解决这个问题 @trentcl 但游标有点矫枉过正,因为我只需要重新启动迭代器几次,而且看起来应该可以通过使用闭包和迭代器在一起。事实上,游标有一个有趣的优势,即用户无法提供在不同运行时返回不同内容的闭包。
-
我的意思是数据库意义上的“视图”,所以我有一个
HashMap<X,(Y,Z)>,但也许我想根据X过滤它,只给函数一个Iterator<Item=&mut Z>。这与&mut C: IntoIterator技巧不兼容,这可能会分散注意力。