2016-09-19 64 views
3

迭代器方法take_while以閉包爲參數。如何將盒裝封蓋傳遞給`take_while`?

例如:

fn main() { 
    let s = "hello!"; 
    let iter = s.chars(); 
    let s2 = iter.take_while(|x| *x != 'o').collect::<String>(); 
    //      ^^^^^^^^^^^^^ 
    //       closure 

    println!("{}", s2); // hell 
} 

playground link

這是對簡單的關閉,但是如果我想一個更復雜的斷言,我不希望它直接在take_while論點寫。相反,我想從函數返回閉包。

我似乎無法正常工作。這是我天真的嘗試:

fn clos(a: char) -> Box<Fn(char) -> bool> { 
    Box::new(move |b| a != b) 
} 

fn main() { 
    // println!("{}", clos('a')('b')); // <-- true 
    //      ^--- Using the closure here is fine 
    let s = "hello!"; 
    let mut iter = s.chars(); 
    let s2 = iter.take_while(clos('o')).collect::<String>(); 
    //       ^--- This causes lots of issues 

    println!("{}", s2); 
} 

playground link

但是,它會導致已經證明,很難理解的錯誤:

error[E0277]: the trait bound `for<'r> Box<std::ops::Fn(char) -> bool>: std::ops::FnMut<(&'r char,)>` is not satisfied 
    --> <anon>:11:23 
    | 
11 |   let s2 = iter.take_while(clos('o')).collect::<String>(); 
    |      ^^^^^^^^^^ trait `for<'r> Box<std::ops::Fn(char) -> bool>: std::ops::FnMut<(&'r char,)>` not satisfied 

error[E0277]: the trait bound `for<'r> Box<std::ops::Fn(char) -> bool>: std::ops::FnOnce<(&'r char,)>` is not satisfied 
    --> <anon>:11:23 
    | 
11 |   let s2 = iter.take_while(clos('o')).collect::<String>(); 
    |      ^^^^^^^^^^ trait `for<'r> Box<std::ops::Fn(char) -> bool>: std::ops::FnOnce<(&'r char,)>` not satisfied 
    | 
    = help: the following implementations were found: 
    = help: <Box<std::boxed::FnBox<A, Output=R> + 'a> as std::ops::FnOnce<A>> 
    = help: <Box<std::boxed::FnBox<A, Output=R> + Send + 'a> as std::ops::FnOnce<A>> 

error: no method named `collect` found for type `std::iter::TakeWhile<std::str::Chars<'_>, Box<std::ops::Fn(char) -> bool>>` in the current scope 
    --> <anon>:11:47 
    | 
11 |   let s2 = iter.take_while(clos('o')).collect::<String>(); 
    |            ^^^^^^^ 
    | 
    = note: the method `collect` exists but the following trait bounds were not satisfied: `Box<std::ops::Fn(char) -> bool> : std::ops::FnMut<(&char,)>`, `std::iter::TakeWhile<std::str::Chars<'_>, Box<std::ops::Fn(char) -> bool>> : std::iter::Iterator` 

error: aborting due to 3 previous errors 

我已經嘗試了一些其他的事情,包括使用FnBox,但它沒有工作。我沒有使用過多的閉包,所以我非常想知道發生了什麼問題,以及如何解決它。

Related

回答

7

有在代碼中的兩個問題。

首先,take_while通過引用該函數傳遞值(請注意where P: FnMut(&Self::Item) -> bool中的&),而您的閉包期望按值接收。

fn clos(a: char) -> Box<Fn(&char) -> bool> { 
    Box::new(move |&b| a != b) 
} 

還有這麼Box<Fn(&char) -> bool>沒有實現FnMut(&char) -> bool問題。如果我們看一下對FnMut的文件,我們會看到,標準庫提供了這些實現:

impl<'a, A, F> FnMut<A> for &'a F where F: Fn<A> + ?Sized 
impl<'a, A, F> FnMut<A> for &'a mut F where F: FnMut<A> + ?Sized 

OK,所以FnMut針對於Fn實現的參考實現。我們手中有一個Fn特質對象,它實現Fn,所以沒關係。我們只需要將Box<Fn>變成&Fn。我們首先需要對產生左值的盒子進行解引用,然後參考這個左值產生一個&Fn

fn main() { 
    let s = "hello!"; 
    let iter = s.chars(); 
    let s2 = iter.take_while(&*clos('o')).collect::<String>(); 
    println!("{}", s2); 
} 
+1

這是Rust中那些確實需要更多覆蓋的東西之一。我自己已經跑了10倍左右的確切問題,並且仍然無法通過文檔/論壇進行一點搜索來尋找解決方案。 –

+0

@SimonWhitehead我同意。我很喜歡學習Rust,但是像這樣的事情可能需要我花幾個小時才能弄清楚(然後我可能甚至不理解該解決方案)。我知道他們非常努力地保持Rust錯誤非常有幫助,但在線文檔可能會更好。我想這是所有年輕語言都有的問題。 – Liam

相關問題