【问题标题】:How can I write a function that takes a collection of closures?如何编写一个带有一组闭包的函数?
【发布时间】:2017-07-30 10:20:25
【问题描述】:

我正在尝试编写一个函数,该函数采用Fn() -> () 类型的闭包集合,即每个闭包不接受任何参数,不返回任何内容(我希望它们实际上是FnOnce,以便移动它的所有环境到闭包对象中)。

我尝试了很多方法(比如使用Box<Fn() -> ()>&'static),但我就是无法正常工作。

我在Rust Playground 中创建了一个要点,以大致展示我正在尝试做的事情。

这是简化的代码:

fn run_all_tests<I>(tests: I)
where
    I: IntoIterator<Item = Box<FnOnce() -> ()>>,
{
}

fn main() {
    let examples = [1, 2, 3];

    run_all_tests(examples.iter().map(
        |ex| Box::new(move |ex| assert!(ex > 0)),
    ));
}

错误:

error[E0271]: type mismatch resolving `<[closure@src/main.rs:11:9: 11:49] as std::ops::FnOnce<(&{integer},)>>::Output == std::boxed::Box<std::ops::FnOnce() + 'static>`
  --> src/main.rs:10:5
   |
10 |     run_all_tests(examples.iter().map(
   |     ^^^^^^^^^^^^^ expected closure, found trait std::ops::FnOnce
   |
   = note: expected type `std::boxed::Box<[closure@src/main.rs:11:23: 11:48]>`
              found type `std::boxed::Box<std::ops::FnOnce() + 'static>`
   = note: required because of the requirements on the impl of `std::iter::Iterator` for `std::iter::Map<std::slice::Iter<'_, {integer}>, [closure@src/main.rs:11:9: 11:49]>`
   = note: required by `run_all_tests`

【问题讨论】:

    标签: rust


    【解决方案1】:

    代码有几个问题:

    1. 你的盒装闭包接受一个参数ex,但特征FnOnce() 不接受任何参数。参数ex 也隐藏了外部闭包中的参数ex,所以我假设您的意思是内部闭包不带参数:move || assert!(ex &gt; 0)

    2. ex &gt; 0 中的类型不匹配,因为将引用与非引用进行了比较。可以通过在模式匹配期间取消引用外部闭包参数来修复:|&amp;ex| ....

    3. 类型推断不够强大,无法发现map 返回的迭代器应该超过Box&lt;FnOnce()&gt; 而不是Box&lt;unique closure object&gt;。您可以添加显式强制转换来解决此问题:Box::new(move || assert!(ex &gt; 0)) as Box&lt;FnOnce()&gt;

    4. 此时,代码将编译,但由于语言限制,当您添加对盒装FnOnce() 的调用时会出现编译错误。见"cannot move a value of type FnOnce" when moving a boxed function。在夜间 Rust 中,您可以将 FnOnce 更改为 FnBox。否则,您可以改用 FnMut 或使用该问题中的一种解决方法。还有另一种解决方法,它基于给定in the Rust book 定义一个额外的特征(参见清单 20-20 和清单 20-21 之间的部分)。

    这是使用FnBox的固定代码:

    #![feature(fnbox)]
    use std::boxed::FnBox;
    
    fn run_all_tests<I>(tests: I)
    where
        I: IntoIterator<Item = Box<FnBox()>>,
    {
        for t in tests {
            t();
        }
    }
    
    fn main() {
        let examples = [1, 2, 3];
    
        run_all_tests(examples.iter().map(|&ex| {
            Box::new(move || assert!(ex > 0)) as Box<FnBox()>
        }));
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-28
      • 1970-01-01
      • 1970-01-01
      • 2016-04-30
      • 1970-01-01
      • 1970-01-01
      • 2012-05-06
      • 2018-08-03
      相关资源
      最近更新 更多