【问题标题】:Return and consume an iterator of mutable references from a closure从闭包返回并使用可变引用的迭代器
【发布时间】: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) 我们的 &amp;mut Self 需要生命周期,所以我们可以这样写:

fn batch_normalization<I, F: FnMut() -> I>(f: F)
where
    I: Iterator<Item = BorrowMut<Self>> + DoubleEndedIterator + ExactSizeIterator

我已经尝试过各种类似的公式,但没有一个完全奏效,主要是因为Item 比迭代器寿命更长。

我们应该像&amp;'a mut C: IntoIterator&lt;Item = &amp;'a mut T&gt; 那样解决这个问题,在FnMut 中明确地将项目的生命周期与&amp;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&lt;Item = &amp;mut T&gt;?是否应该总是使用一些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&lt;X,(Y,Z)&gt;,但也许我想根据X 过滤它,只给函数一个Iterator&lt;Item=&amp;mut Z&gt;。这与 &amp;mut C: IntoIterator 技巧不兼容,这可能会分散注意力。

标签: iterator rust lifetime


【解决方案1】:

由于Fn* trait 不支持将返回类型绑定到其self 参数的生命周期,因此无法通过闭包精确地做到这一点。现在,Fn* 特征读取

pub trait FnOnce<Args> {
    type Output;
    extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}
pub trait FnMut<Args>: FnOnce<Args> {
    extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}
pub trait Fn<Args>: FnMut<Args> {
    extern "rust-call" fn call(&self, args: Args) -> Self::Output;
}

但这需要这些特征读起来像

pub trait FnOnce<Args> {
    type Output<'fn>;
    extern "rust-call" fn call_once(self, args: Args) -> Self::Output<'static>;
}
pub trait FnMut<Args>: FnOnce<Args> {
    extern "rust-call" fn call_mut<'fn>(&'fn mut self, args: Args) -> Self::Output<'fn>;
}
pub trait Fn<Args>: FnMut<Args> {
    extern "rust-call" fn call<'fn>(&'fn self, args: Args) -> Self::Output<'fn>;
}

这些是不稳定的接口,因此它们最终可能会通过 RFC 流程进行更改,可能使用一些特殊的 'fnliftime 语法,如 FnMut() -&gt; impl Iterator&lt;Item = &amp;'fn mut Self&gt;,或者甚至可能使用参数化的类型参数 ArgsArgs&lt;'fn&gt;。 Rust internals 是这个问题的合适论坛。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-15
    • 2021-05-09
    • 1970-01-01
    • 2015-01-22
    相关资源
    最近更新 更多