如何将盒装闭包传递给`take_while`?

Lia*_*iam 5 closures iterator rust

迭代器方法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
}
Run Code Online (Sandbox Code Playgroud)

游乐场链接

这对于简单的闭包很好,但是如果我想要一个更复杂的谓词,我不想直接在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);
}
Run Code Online (Sandbox Code Playgroud)

游乐场链接

但是,它导致的错误已经证明难以理解:

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
Run Code Online (Sandbox Code Playgroud)

我尝试了一些其他的东西,包括使用FnBox,但它没有用.我没有那么多地使用闭包,所以我真的想了解出了什么问题,以及如何修复它.

有关

Fra*_*gné 9

您的代码中存在两个问题.

首先,take_while通过引用传递值到函数(注意&in where P: FnMut(&Self::Item) -> bool),而你的闭包期望按值接收它.

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

然后就是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
Run Code Online (Sandbox Code Playgroud)

好的,所以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);
}
Run Code Online (Sandbox Code Playgroud)

  • 这是 Rust 中确实需要更多覆盖的事情之一。我自己已经遇到过大约 10 次这个确切的问题,但如果不通过文档/论坛进行一些搜索,仍然很难找到解决方案。 (2认同)