我有一个元组向量:

let l = vec![(0, 1), (2, 3)];

我希望得到std::iter::Map,它提取每个元组的第一个元素.天真的是,这招奏效了:

let m: std::iter::Map<_, _> = l.iter().map(|e| e.0);

现在,我想改为扩展Iterator以使此伪代码工作:

let m: std::iter::Map<_, _> = l.iter().tuple_first();

try 1

我试着返回impl Iterator:

trait TupleExtractor<'a, T1: 'a, T2: 'a>: Iterator<Item = &'a (T1, T2)> {
    fn tuple_first(self) -> impl Iterator<Item = &'a T1>;
}

impl<'a, T1: 'a + Copy, T2: 'a, I> TupleExtractor<'a, T1, T2> for I
where
    I: Iterator<Item = &'a (T1, T2)>,
{
    fn tuple_first(self) -> impl Iterator<Item = &'a T1> {
        self.map(|e| e.0)
    }
}

此代码无法编译,因为

`impl Trait` only allowed in function and inherent method return types, not in trait method return types

try 2

我试图显式返回std::iter::Map,而不是impl Iterator.然而,出于同样的原因,这段代码也不能编译.

trait TupleExtractor<'a, T1: 'a, T2: 'a>: Iterator<Item = &'a (T1, T2)> {
    fn tuple_first(
        self,
    ) -> std::iter::Map<impl Iterator<Item = &'a (T1, T2)>, impl FnMut(&'a (T1, T2)) -> T1>;
}

impl<'a, T1: 'a + Copy, T2: 'a, I> TupleExtractor<'a, T1, T2> for I
where
    I: Iterator<Item = &'a (T1, T2)>,
{
    fn tuple_first(self) -> std::iter::Map<I, impl FnMut(&'a (T1, T2)) -> T1> {
        self.map(|e| e.0)
    }
}

try 3

由于try 2不起作用的原因是我不能指定std::iter::Map的类型参数,所以我try 使它们成为类型参数:

trait TupleExtractor<'a, T1: 'a, T2: 'a, A, B>: Iterator<Item = &'a (T1, T2)> {
    fn tuple_first(self) -> std::iter::Map<A, B>;
}

impl<'a, T1: 'a + Copy, T2: 'a, I, F> TupleExtractor<'a, T1, T2, I, F> for I
where
    I: Iterator<Item = &'a (T1, T2)>,
    F: FnMut(&'a (T1, T2)) -> T1,
{
    fn tuple_first(self) -> std::iter::Map<I, F> {
        self.map(|e: &'a (T1, T2)| e.0)
    }
}

此代码无法编译,因为 the closure |e: &'a (T1, T2)| e.0 is not of the type F:

= note: expected type parameter `F`
        found closure `[closure@src/main.rs:38:18: 38:35]`
= help: every closure has a distinct type and so could not always match the caller-chosen type of parameter `F`

如何从特征法返回std::iter::Map?我不想用Box.

推荐答案

我能想到的唯一解决方法是不允许使用捕获闭包(因为它们的类型还不能在特征中命名),而是使用函数指针.然后,您可以使用关联的类型来定义您的特征,而不是返回位置Iml特征.这显然限制了可能的实现,但它仍然允许您所展示的用例:

trait TupleExtractor<'a, T1: 'a, T2: 'a>: Iterator<Item = &'a (T1, T2)> {
    type Mapper: Iterator<Item = T1>;
    fn tuple_first(
        self,
    ) -> Self::Mapper;
    //   ^^^^^^^^^^^^ replace return position impl trait with associated type
}

impl<'a, T1: 'a + Copy, T2: 'a, I> TupleExtractor<'a, T1, T2> for I
where
    I: Iterator<Item = &'a (T1, T2)>,
{
    type Mapper = std::iter::Map<I, fn(&'a (T1, T2)) -> T1>;
    //                              ^^^^^^^^^^^^^^^^^^^^^^ we can actually name this type
    fn tuple_first(self) -> Self::Mapper {
        // implementation stays the same because this is just an anonymous function
        // not a closure
        self.map(|e| e.0)
    }
}

#[test]
fn test_tuple_first() {
    let v = vec![(1, 'a'), (2, 'b'), (3, 'b')];
    assert!(v.iter().tuple_first().eq([1, 2, 3].into_iter()))
}

如果Mapper类型需要为其他潜在的特征方法提供更好的可扩展性,您可以使其泛型于它产生的项的类型:

trait TupleExtractor<'a, T1: 'a, T2: 'a>: Iterator<Item = &'a (T1, T2)> {
    type Mapper<T>: Iterator<Item = T>
    where
        T: 'a;
    
    fn tuple_first(
        self,
    ) -> Self::Mapper<T1>;
    
    fn tuple_second(
        self,
    ) -> Self::Mapper<T2>;
}

impl<'a, T1: 'a + Copy, T2: 'a + Copy, I> TupleExtractor<'a, T1, T2> for I
where
    I: Iterator<Item = &'a (T1, T2)>,
{
    type Mapper<T> = std::iter::Map<I, fn(&'a (T1, T2)) -> T> where T: 'a;
    
    fn tuple_first(self) -> Self::Mapper<T1> {
        self.map(|e| e.0)
    }
    
    fn tuple_second(self) -> Self::Mapper<T2> {
        self.map(|e| e.1)
    }
}

#[test]
fn test_tuple_first() {
    let v = vec![(1, 'a'), (2, 'b'), (3, 'b')];
    assert!(v.iter().tuple_first().eq([1, 2, 3].into_iter()))
}

#[test]
fn test_tuple_second() {
    let v = vec![(1, 'a'), (2, 'b'), (3, 'b')];
    assert!(v.iter().tuple_second().eq(['a', 'b', 'b'].into_iter()))
}

Rust相关问答推荐

有没有方法处理rust中嵌套的ok_or()?

移植带有可变borrow 的C代码-卸载期间错误(nappgui示例)

如何在不安全的代码中初始化枚举 struct

如何使用 list 在Rust for Windows中编译?

同时从不同线程调用DLL的不同函数会出现分段错误或产生STATUS_STACK_BUFFER_OVERRUN

如何定义实现同名但返回类型不同的 struct 的函数

S在Cargo.toml中添加工作空间开发依赖关系的正确方法是什么?

无法实现整型类型的泛型FN

Rust面向对象设计模式

如何在 Rust 中打印 let-else 语句中的错误?

为什么是&mut发送?线程如何在安全的 Rust 中捕获 &mut?

Rust中的一生语法有什么作用?

rust tokio::spawn 在 mutexguard 之后等待

从现有系列和 map 值创建新系列

实现不消费的迭代器

相交着色器从 SSBO 中读取零

如何从 many0 传播 Nom 失败上下文?

在 Rust 中组合特征的不同方法是否等效?

类型参数不受 impl 特征、自身类型或谓词的约束

来自外部函数的future 内部可变引用