如何返回对filter_map的调用结果

时间:2015-12-25 04:14:44

标签: iterator rust stack-memory

这里我有一个(char,usize)对的矢量vec,我想写一个函数

fn take_lt(&'a vec, cutoff: usize) -> Iterator<'a, char>

返回匹配值小于cutoff的字符上的迭代器。

  1. 有没有办法做到这一点,没有分配东西堆的开销(即装箱Fn环境或创建另一个向量)?
  2. 有没有办法做到这一点 无需明确写出可怕的相关回报 类型?
  3. 在尝试了许多不同的方法(其中一些已经编译,但所有这些都涉及我希望避免的堆分配)之后,我提出了:

    use std::iter::repeat;
    use std::iter::FilterMap;
    use std::iter::Zip;
    use std::iter::Repeat;
    use std::slice;
    
    fn take_lt<'a>(vec: &'a[(char, usize)], cutoff: usize) -> FilterMap<Zip<slice::Iter<'a, (char, usize)>, Repeat<usize>>, &fn((&(char, usize), usize)) -> Option<char>> {
        fn cmp_fun((&(x, a), b): (&(char, usize), usize)) -> Option<char> {
            if a < b {
                Some(x)
            } else {
                None
            }
        }
        vec.iter().zip(repeat(cutoff)).filter_map(&cmp_fun)
    }
    

    这很接近,但我明白了:

    src/lib.rs:15:47: 15:55 error: mismatched types:
     expected `&fn((&(char, usize), usize)) -> core::option::Option<char>`,
        found `&fn((&(char, usize), usize)) -> core::option::Option<char> {take_lt::cmp_fun}`
    (expected fn pointer,
        found fn item) [E0308]
    src/lib.rs:15     vec.iter().zip(repeat(cutoff)).filter_map(&cmp_fun)
                                                                ^~~~~~~~
    

    有点谷歌搜索建议我尝试将函数项转换为函数指针,如:

    vec.iter().zip(repeat(cutoff)).filter_map(&(cmp_fun as fn((&(char, usize), usize)) -> Option<char>))
    

    但是失败了:

    src/lib.rs:15:49: 15:103 error: borrowed value does not live long enough
    src/lib.rs:15     vec.iter().zip(repeat(cutoff)).filter_map(&(cmp_fun as fn((&(char, usize), usize)) -> Option<char>))
                                                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    src/lib.rs:7:167: 16:2 note: reference must be valid for the lifetime 'a as defined on the block at 7:166...
    src/lib.rs: 7 fn take_lt<'a>(vec: &'a[(char, usize)], cutoff: usize) -> FilterMap<Zip<slice::Iter<'a, (char, usize)>, Repeat<usize>>, &fn((&(char, usize), usize)) -> Option<char>> {
    src/lib.rs: 8     fn cmp_fun((&(x, a), b): (&(char, usize), usize)) -> Option<char> {
    src/lib.rs: 9         if a < b {
    src/lib.rs:10             Some(x)
    src/lib.rs:11         } else {
    src/lib.rs:12             None
                  ...
    src/lib.rs:7:167: 16:2 note: ...but borrowed value is only valid for the block at 7:166
    src/lib.rs: 7 fn take_lt<'a>(vec: &'a[(char, usize)], cutoff: usize) -> FilterMap<Zip<slice::Iter<'a, (char, usize)>, Repeat<usize>>, &fn((&(char, usize), usize)) -> Option<char>> {
    src/lib.rs: 8     fn cmp_fun((&(x, a), b): (&(char, usize), usize)) -> Option<char> {
    src/lib.rs: 9         if a < b {
    src/lib.rs:10             Some(x)
    src/lib.rs:11         } else {
    src/lib.rs:12             None
                  ...
    

2 个答案:

答案 0 :(得分:3)

你很亲密:

// type alias for the return type (optional, I just find it a bit
// optically easier to work with). I added:
// a 'a lifetime parameter that ties the return Iter lifetime to the 
// input slice
// a 'static lifetime for the function pointer
type RetTake<'a> = FilterMap<Zip<slice::Iter<'a, (char, usize)>, 
    Repeat<usize>>, &'static fn((&(char, usize), usize)) -> Option<char>>;

fn take_lt<'a>(vec: &'a[(char, usize)], cutoff: usize) -> RetTake {
    fn cmp_fun((&(x, a), b): (&(char, usize), usize)) -> Option<char> {
        if a < b {
            Some(x)
        } else {
            None
        }
    }

    // I think this explicit static binding 
    // used not to be necessary, but I now can't get rustc
    // to give the reference to the function pointer the static lifetime
    // it needs otherwise 
    static F: fn((&(char, usize), usize)) -> Option<char> = cmp_fun;
    vec.iter().zip(repeat(cutoff)).filter_map(&F)
}

作为替代方案,您可以创建自己的结构,实现所需的迭代器逻辑并返回该结构。例如:

struct CutoffIterator<'a> {
    iter: slice::Iter<'a, (char, usize)>,
    cutoff: usize,
}

impl<'a> Iterator for CutoffIterator<'a> {
    type Item = char;

    fn next(&mut self) -> Option<char> {
        loop {
            match self.iter.next() {
                Some(&(x, a)) if a < self.cutoff => return Some(x),
                Some(&(_, a)) if a >= self.cutoff => continue,
                _ => return None
            }
        }
    }
}

fn take_lt2(vec: &[(char, usize)], cutoff: usize) -> CutoffIterator {
    CutoffIterator { iter: vec.iter(), cutoff: cutoff }
}

答案 1 :(得分:1)

每个函数都有一个独特的,不同的类型,与fn类型兼容。这反映了闭包也有不同类型的事实。这就是found fn item编译器的含义:它没有找到您在返回类型中指定的fn类型,而是cmp_fun函数的唯一类型。

fn类型已经是指针,因此不需要(至少在您的情况下)引用fn;你可以直接拿fn。通过这样做,编译器将隐式地将函数转换为更通用的fn类型。

fn take_lt<'a>(vec: &'a[(char, usize)], cutoff: usize) -> FilterMap<Zip<slice::Iter<'a, (char, usize)>, Repeat<usize>>, fn((&(char, usize), usize)) -> Option<char>> {
    fn cmp_fun((&(x, a), b): (&(char, usize), usize)) -> Option<char> {
        if a < b {
            Some(x)
        } else {
            None
        }
    }
    vec.iter().zip(repeat(cutoff)).filter_map(cmp_fun)
}