如何从函数返回Filter迭代器?

时间:2014-12-25 11:48:20

标签: generics iterator rust

我想要这样的东西:

fn filter_one<'a, T: Int>(input: &'a Vec<T>) -> ??? {
    input.iter().filter(|&x| x == Int::one())
}

该功能的返回类型是什么? (我想退回迭代器)

(我希望这不是太明显,我现在已经尝试了半个小时而且开始感到沮丧:p)

修改

我尝试按照here =&gt;中的说明进行操作playpen link

编译器给出了以下错误:

<anon>:5:1: 7:2 error: the trait `core::kinds::Sized` is not implemented for the type `for<'r> core::ops::Fn(&'r T) -> bool + 'a`
<anon>:5 fn filter_one<'a, T: Int>(input: &'a Vec<T>) -> Filter<&T, Iter<'a, T>, Fn(&T) -> bool>{
<anon>:6     input.iter().filter(|&x| x == Int::one())
<anon>:7 }
<anon>:5:1: 7:2 note: required by `core::iter::Filter`
<anon>:5 fn filter_one<'a, T: Int>(input: &'a Vec<T>) -> Filter<&T, Iter<'a, T>, Fn(&T) -> bool>{
<anon>:6     input.iter().filter(|&x| x == Int::one())
<anon>:7 }
<anon>:5:1: 7:2 error: the trait `for<'r> core::ops::Fn(&'r &'a T) -> bool` is not implemented for the type `for<'r> core::ops::Fn(&'r T) -> bool + 'a`
<anon>:5 fn filter_one<'a, T: Int>(input: &'a Vec<T>) -> Filter<&T, Iter<'a, T>, Fn(&T) -> bool>{
<anon>:6     input.iter().filter(|&x| x == Int::one())
<anon>:7 }
<anon>:5:1: 7:2 note: required by `core::iter::Filter`
<anon>:5 fn filter_one<'a, T: Int>(input: &'a Vec<T>) -> Filter<&T, Iter<'a, T>, Fn(&T) -> bool>{
<anon>:6     input.iter().filter(|&x| x == Int::one())
<anon>:7 }
error: aborting due to 2 previous errors
playpen: application terminated with error code 101

如何告诉rustc Fn(&T) -> boolSized?

2 个答案:

答案 0 :(得分:19)

Rust 1.26

fn filter_one(input: &[u8]) -> impl Iterator<Item = &u8> {
    input.iter().filter(|&&x| x == 1)
}

fn main() {
    let nums = vec![1, 2, 3, 1, 2, 3];
    let other: Vec<_> = filter_one(&nums).collect();
    println!("{:?}", other);
}

Rust 1.0

fn filter_one<'a>(input: &'a [u8]) -> Box<Iterator<Item = &'a u8> + 'a> {
    Box::new(input.iter().filter(|&&x| x == 1))
}

fn main() {
    let nums = vec![1, 2, 3, 1, 2, 3];
    let other: Vec<_> = filter_one(&nums).collect();
    println!("{:?}", other);
}

此解决方案需要额外分配。我们创建了一个盒装特征对象。这里,对象的大小总是已知的(它只是一个或两个指针),但不需要知道堆中对象的大小。

作为Vladimir Matveev points out,如果您的谓词逻辑不需要来自环境的任何信息,您可以使用函数而不是闭包:

use std::{iter::Filter, slice::Iter};

fn filter_one<'a>(input: &'a [u8]) -> Filter<Iter<u8>, fn(&&u8) -> bool> {
    fn is_one(a: &&u8) -> bool {
        **a == 1
    }

    input.iter().filter(is_one)
}

fn main() {
    let nums = vec![1, 2, 3, 1, 2, 3];
    let other: Vec<_> = filter_one(&nums).collect();
    println!("{:?}", other);
}

答案 1 :(得分:11)

不幸的是,不可能返回依赖于闭包的迭代器(特别是闭包,函数可以正常工作;见下文),就像filter()map()适配器返回的迭代器一样。这就是原因。

这是filter()迭代器扩展方法的签名:

fn filter<P>(self, predicate: P) -> Filter<A, Self, P> where P: FnMut(&A) -> bool

请注意,closure参数接受任何实现FnMut特征的类型。没错,大多数(如果不是全部)标准库最近都被切换为使用未装箱的封闭而不是旧盒装封闭。

但是,此签名意味着,如果您指定闭包作为filter()的参数,就像您在filter_one()中所做的那样:

input.iter().filter(|&x| x == Int::one())

然后在此特定调用P的单态化之后将成为由编译器生成的一些匿名未命名类型。因为它是未命名的,当然,你不能在类型签名中指定它,因此,你也不能指定一个依赖于未装箱的闭包的迭代器类型 - 你就是不会知道写什么作为Filter<A, I, P>的第三个参数。

你可以通过使用函数而不是闭包来解决这个问题,这应该足以满足你的用例:

use std::slice::Items;
use std::iter::Filter;
use std::num::Int;

fn filter_one<T: Int>(input: &[T]) -> Filter<&T, Items<T>, fn(&&T) -> bool> {
    fn equal_to_one<U: Int>(&&x: &&U) -> bool { x == Int::one() }
    input.iter().filter(equal_to_one::<T>)
}

fn main() {
    let v = [1i, 2, 3];
    let r: Vec<int> = filter_one(v.as_slice()).map(|x| *x).collect();
    println!("{}", r);
}

请注意,我也已将&Vec<T>更改为&[T] - 您永远不应使用&Vec<T>,因为它会不必要地限制代码的灵活性。

但是,对于闭包的更一般情况,如果没有抽象返回类型,则无法执行此操作。有一个建议添加它们,但它被推迟到1.0之后。