2015-12-25 2 views
2

Здесь у меня есть вектор VEC из (полукокса, usize) пар, и я хочу, чтобы написать функциюКак вернуть результат вызова filter_map

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

который возвращает итератор символов, соответствующих значений меньше отсечки.

  1. Есть ли способ сделать это без накладных расходов, выделяя что-то в кучу (т.е. боксирование среды Fn или создание другого вектора)?
  2. Есть ли способ сделать это
    без необходимости явно выписывать отвратительное связанное с ним возвращение типа?

После попытки этого много различных способов (несколько из которых составлено, но все из которых участвуют распределение кучи, которые я хотел бы избежать), я придумал:

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 
       ... 
+0

Дубликат http://stackoverflow.com/questions/27535289/correct-way-to-return-an-iterator. – Shepmaster

ответ

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 } 
} 
+0

Я раньше этого не видел (статический F ...). Не могли бы вы указать мне, где я могу узнать больше об этом? – dspyz

+0

Также есть способ избежать взлома .zip (repeat()) и просто сохранить среду как часть итератора напрямую? – dspyz

+0

Статические переменные @dspyz рассматриваются в разделе [const и static в книге] (https://doc.rust-lang.org/stable/book/const-and-static.html). F - статическая переменная с типом «указатель на функцию» –

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) 
} 
Смежные вопросы