我想定义一个square()没有不必要的运行时开销(没有dyn关键字)的惰性方法,可以在 any 上调用Iterable<Item = u8>并返回另一个Iterable<Item = u8>,如下所示:
fn main() {
vec![1, 2, 3, 4, 5]
.iter()
.filter(|x| x > 1)
.squared()
.filter(|x| x < 20);
}
Run Code Online (Sandbox Code Playgroud)
我知道如何定义squared()为独立函数:
fn squared<I: Iterator<Item = u8>>(iter: I) -> impl Iterator<Item = u8> {
iter.map(|x| x * x)
}
Run Code Online (Sandbox Code Playgroud)
Iterator<Item = u8>不过,要定义该方法,我必须首先定义一个trait. 这就是我挣扎的地方?-?traits 不能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)
}
}
Run Code Online (Sandbox Code Playgroud)
我在解决这个问题上有很多失败的尝试,包括改变squaredto的返回类型Map<u8, fn(u8) -> u8>和修改IntoIterables,但到目前为止没有任何效果。任何帮助将不胜感激!
首先,您的输出迭代器可能应该是关联类型而不是特征参数,因为该类型是特征的输出(它不是调用者可以控制的)。
trait Squarable {
type Output: Iterator<Item = u8>;
fn squared(self) -> I;
}
Run Code Online (Sandbox Code Playgroud)
话虽如此,有几种不同的可能方法可以解决这个问题,每种方法都有不同的优点和缺点。
第一种是使用特征对象,例如dyn Iterator<Item = u8>,在运行时擦除类型。这需要轻微的运行时成本,但绝对是当今稳定 Rust 中最简单的解决方案:
trait Squarable {
type Output: Iterator<Item = u8>;
fn squared(self) -> I;
}
Run Code Online (Sandbox Code Playgroud)
在稳定的 Rust 中,从 trait 用户的角度来看,这绝对是最干净的,但是它需要更多的代码来实现,因为你需要编写自己的迭代器类型。然而,对于一个简单的map迭代器来说,这是非常直接的:
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))
}
}
Run Code Online (Sandbox Code Playgroud)
Map类型<I as Iterator>::map(f)有一个 type std::iter::Map<I, F>,所以如果F映射函数的类型是已知的,我们可以显式使用该类型,而无需运行时成本。这确实将特定类型公开为函数返回类型的一部分,这使得将来在不破坏依赖代码的情况下更难替换。在大多数情况下,函数也不为人所知;在这种情况下,我们可以使用,F = fn(u8) -> u8因为该函数不保留任何内部状态(但通常不起作用)。
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)
}
}
Run Code Online (Sandbox Code Playgroud)
上面的一个替代方法是给特征一个关联类型。这仍然具有必须知道函数类型的限制,但它更通用一点,因为Map<...>类型与实现相关,而不是与特征本身相关。
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)
}
}
Run Code Online (Sandbox Code Playgroud)
impl在相关类型这类似于上面的“使用关联类型”,但您可以完全隐藏实际类型,除了它是一个迭代器这一事实。我个人认为这是首选的解决方案,但不幸的是它仍然不稳定(取决于type_alias_impl_trait功能),因此您只能在夜间 Rust 中使用它。
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)
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
326 次 |
| 最近记录: |