在迭代器上创建方法,该方法在Rust中返回迭代器

马塞尔

我想定义一个square()没有不必要的运行时开销(没有dyn关键字)的惰性方法,可以在任何方法上调用它Iterable<Item = u8>并返回另一个方法Iterable<Item = u8>,如下所示:

fn main() {
    vec![1, 2, 3, 4, 5]
        .iter()
        .filter(|x| x > 1)
        .squared()
        .filter(|x| x < 20);
}

我知道如何定义squared()为独立功能:

fn squared<I: Iterator<Item = u8>>(iter: I) -> impl Iterator<Item = u8> {
    iter.map(|x| x * x)
}

为了定义该方法Iterator<Item = u8>,我必须先定义一个trait这就是我苦苦挣扎的地方-特性不能impl在返回值中使用关键字。

我正在寻找类似以下内容的内容,但无法正常工作:

trait Squarable<I: Iterator<Item = u8>> {
    fn squared(self) -> I;
}

impl<I, J> Squarable<I> for J
where
    I: Iterator<Item = u8>,
    J: Iterator<Item = u8>,
{
    fn squared(self) -> I {
        self.map(|x| x * x)
    }
}

我为解决该问题进行了许多失败的尝试,包括更改squaredto的返回类型Map<u8, fn(u8) -> u8>并使用IntoIterables进行修改,但到目前为止没有任何效果。任何帮助将不胜感激!

Frxstrem

首先,您的输出迭代器可能应该是关联的类型,而不是特征参数,因为该类型是特征的输出(调用者无法控制)。

trait Squarable {
    type Output: Iterator<Item = u8>;
    fn squared(self) -> I;
}

话虽这么说,有几种不同的方法可以解决此问题,每种方法都有各自的优点和缺点。

使用特征对象

第一种是使用特质对象,例如dyn Iterator<Item = u8>,在运行时擦除类型。这会花费少量的运行时成本,但是绝对是当今稳定的Rust中最简单的解决方案:

trait Squarable {
    fn squared(self) -> Box<dyn Iterator<Item = u8>>;
}

impl<I: 'static + Iterator<Item = u8>> Squarable for I {
    fn squared(self) -> Box<dyn Iterator<Item = u8>> {
        Box::new(self.map(|x| x * x))
    }
}

使用自定义迭代器类型

从特质用户的角度来看,在稳定的锈迹中,这绝对是最干净的方法,但是由于需要编写自己的迭代器类型,因此需要更多代码来实现。但是,对于一个简单的map迭代器来说,这很简单:

trait Squarable: Sized {
    fn squared(self) -> SquaredIter<Self>;
}

impl<I: Iterator<Item = u8>> Squarable for I {
    fn squared(self) -> SquaredIter<I> {
        SquaredIter(self)
    }
}

struct SquaredIter<I>(I);

impl<I: Iterator<Item = u8>> Iterator for SquaredIter<I> {
    type Item = u8;
    fn next(&mut self) -> Option<u8> {
        self.0.next().map(|x| x * x)
    }
}

使用显式Map类型

<I as Iterator>::map(f)具有类型std::iter::Map<I, F>,因此,如果F知道映射函数的类型,则可以显式使用该类型,而无需花费运行时间。但这确实将特定类型公开为函数的返回类型的一部分,这使得将来在不破坏相关代码的情况下更难替换。在大多数情况下,该功能也不为人所知。在这种情况下,我们可以使用F = fn(u8) -> u8该函数,因为该函数不会保留任何内部状态(但通常不会起作用)。

trait Squarable: Sized {
    fn squared(self) -> std::iter::Map<Self, fn(u8) -> u8>;
}

impl<I: Iterator<Item = u8>> Squarable for I {
    fn squared(self) -> std::iter::Map<Self, fn(u8) -> u8> {
        self.map(|x| x * x)
    }
}

使用关联的类型

上面的替代方法是为特征赋予关联类型。这仍然具有必须知道函数类型的限制,但是由于函数类型Map<...>与实现而不是特征本身绑定在一起,因此更加通用

trait Squarable {
    type Output: Iterator<Item = u8>;
    fn squared(self) -> Self::Output;
}

impl<I: Iterator<Item = u8>> Squarable for I {
    type Output = std::iter::Map<Self, fn(u8) -> u8>;
    fn squared(self) -> Self::Output {
        self.map(|x| x * x)
    }
}

使用impl在相关类型

这类似于上面的“使用关联的类型”,但是除了它是迭代器之外,您还可以完全隐藏实际的类型。我个人认为这是首选的解决方案,但不幸的是,它仍然不稳定(取决于type_alias_impl_trait功能),因此您只能在每晚的Rust中使用它。

#![feature(type_alias_impl_trait)]

trait Squarable {
    type Output: Iterator<Item = u8>;
    fn squared(self) -> Self::Output;
}

impl<I: Iterator<Item = u8>> Squarable for I {
    type Output = impl Iterator<Item = u8>;
    fn squared(self) -> Self::Output {
        self.map(|x| x * x)
    }
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章