为什么将闭包传递给接受函数指针的函数不起作用?

周汉成*_*周汉成 11 rust

Rust编程语言第二版(强调我的):

函数指针实现所有三个闭包特征(Fn,FnMutFnOnce),因此您总是可以将函数指针作为期望闭包的函数的参数传递.最好使用泛型类型和闭包特征之一来编写函数,这样您的函数就可以接受函数或闭包.

将闭包传递给接受函数指针作为参数的函数不会编译:

fn main() {
    let a = String::from("abc");

    let x = || println!("{}", a);

    fn wrap(c: fn() -> ()) -> () {
        c()
    }

    wrap(x);
}
Run Code Online (Sandbox Code Playgroud)
error[E0308]: mismatched types
  --> src/main.rs:10:10
   |
10 |     wrap(x);
   |          ^ expected fn pointer, found closure
   |
   = note: expected type `fn()`
              found type `[closure@src/main.rs:4:13: 4:33 a:_]`
Run Code Online (Sandbox Code Playgroud)

为什么这不起作用?

Den*_*ret 9

封闭不是一种功能.

您可以将函数传递给期望闭包的函数,但没有理由反转为真.

如果您希望能够将闭包和函数作为参数传递,只需为闭包准备它.

例如:

let a = String::from("abc");

let x = || println!("x {}", a);

fn y() {
    println!("y")
}

fn wrap(c: impl Fn()) {
    c()
}

wrap(x); // pass a closure
wrap(y); // pass a function
Run Code Online (Sandbox Code Playgroud)

  • 注意:在 Rust 2018 中,您需要编写 `&dyn Fn() -> ()` 或 `&impl Fn() -> ()`。 (2认同)
  • 不,你不需要**这样做,但你可以.https://play.rust-lang.org/?gist=ad8cae9dfda6281e579f286300e597ed&version=beta&mode=debug&edition=2018你有链接吗? (2认同)
  • 我尝试了“&Fn ..”、“&impl Fn ..”和“&dyn Fn ..”,一切正常。那么它们之间有什么区别呢? (2认同)
  • 引用闭包参数是很不寻常的。通常这会写成“fn wrap(c: impl Fn())”。 (2认同)

She*_*ter 8

在某些情况下,您可以将闭包作为函数指针传递。这有效:

fn main() {
    let x = || {
        let a = String::from("abc");
        println!("{}", a);
    };

    fn wrap(c: fn()) {
        c()
    }

    wrap(x);
}
Run Code Online (Sandbox Code Playgroud)

重要的区别是,不允许封闭从其环境中捕获任何东西。这意味着我们必须防止String跨越封闭边界。

任何不捕获环境的闭包都可以简单地重写为匿名独立函数,然后转换为函数指针。

添加环境后,该环境将不再可转换,并且现有答案中的所有内容均适用。

请注意,声明-> ()是非惯用语,因为如果未指定,则这是默认设置。

也可以看看:

  • @DenysSéguret一个棘手的语义问题!我认为您是正确的,从技术上讲它不是闭包,但它使用的是闭包*语法*。大多数程序员可以忽略这些特定的细节,让编译器找出最佳的实现。 (3认同)
  • 如果没有关闭任何内容,您真的可以称其为关闭吗? (2认同)
  • @DenysSéguret 闭包关闭一组对象,空集是有效集。 (2认同)