【问题标题】:How do I pass a boxed closure to `take_while`?如何将盒装闭包传递给`take_while`?
【发布时间】:2017-01-26 14:26:38
【问题描述】:

迭代器方法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

【问题讨论】:

    标签: iterator rust closures


    【解决方案1】:

    您的代码中有两个问题。

    首先,take_while 通过引用将值传递给函数(注意 where P: FnMut(&amp;Self::Item) -&gt; bool 中的 &amp;),而您的闭包希望通过值接收它。

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

    然后是Box&lt;Fn(&amp;char) -&gt; bool&gt; 没有实现FnMut(&amp;char) -&gt; 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
    

    好的,所以 FnMut 是为引用 Fn 的实现而实现的。我们手中有一个Fn trait 对象,它实现了Fn,所以这很好。我们只需要将Box&lt;Fn&gt; 变成&amp;Fn。我们首先需要取消对框的引用,这会产生一个左值,然后引用这个左值来产生一个&amp;Fn

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

    【讨论】:

    • 这是 Rust 中确实需要更多覆盖的东西之一。我自己已经遇到过这个确切的问题大约 10 次,但如果没有通过文档/论坛进行一点搜索,仍然很难找到解决方案。
    • @SimonWhitehead 我同意。我非常喜欢学习 Rust,但是这样的事情可能需要我几个小时才能弄清楚(然后我什至可能不理解解决方案)。我知道他们非常努力地保持 Rust 错误非常有用,但在线文档可能会更好。我想这是所有年轻语言都有的问题。
    猜你喜欢
    • 2021-08-01
    • 2017-01-12
    • 2022-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-09
    • 1970-01-01
    相关资源
    最近更新 更多