从闭包中返回并使用可变引用的迭代器

时间:2018-08-04 09:09:18

标签: iterator rust lifetime

有没有使用返回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>的方式明确地将项目的生存期与&mut self中的FnMut的生存期联系起来,以解决此问题。用伪代码:

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() { } 
}

1 个答案:

答案 0 :(得分:0)

由于Fn*特性不支持将返回类型绑定到其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流程中进行更改,可能使用某些特殊的'fn语法,例如FnMut() -> impl Iterator<Item = &'fn mut Self>,甚至可能使用类型参数{{1} }设为Args。 Rust内部构件是此问题的合适论坛。