46

In the second edition of The Rust Programming Language (emphasis mine):

Function pointers implement all three of the closure traits (Fn, FnMut, and FnOnce), so you can always pass a function pointer as an argument for a function that expects a closure. It’s best to write functions using a generic type and one of the closure traits so your functions can accept either functions or closures.

Passing a closure to a function which accepts a function pointer as an argument doesn't compile:

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

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

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

    wrap(x);
}
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:_]`

Why does this not work?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
周汉成
  • 1,217
  • 2
  • 13
  • 24
  • Please specify your actual question. (Do you want to know whether it is possible in general, or do you want to have docs why this does not work?) – hellow Oct 08 '18 at 07:16

2 Answers2

37

A closure isn't a function.

You can pass a function to a function expecting a closure, but there's no reason for the reverse to be true.

If you want to be able to pass both closures and functions as argument, just prepare it for closures.

For example:

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
Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • 2
    Note: in Rust 2018, you need to write `&dyn Fn() -> ()` or `&impl Fn() -> ()`. – Sebastian Redl Oct 08 '18 at 08:08
  • 4
    No, you don't **need** to do that, but you can. https://play.rust-lang.org/?gist=ad8cae9dfda6281e579f286300e597ed&version=beta&mode=debug&edition=2018 Do you have a link, that says anything else? – hellow Oct 08 '18 at 08:14
  • 1
    @hellow my understanding is that the distinction should now be explicit and the implicit notation is or will be deprecated – Denys Séguret Oct 08 '18 at 08:16
  • Should, then. I was under the impression that this kind of syntax-breaking change is exactly why the edition was made, but perhaps this was seen as too abrupt. – Sebastian Redl Oct 08 '18 at 08:32
  • @DenysSéguret the book says **you can always pass a function pointer as an argument for a function that expects a closure** – 周汉成 Oct 08 '18 at 08:47
  • 1
    @周汉成 yes, but you were trying the reverse, which isn't possible. – Denys Séguret Oct 08 '18 at 08:48
  • @DenysSéguret yeah..that expression is fuzzy. It's better to say **you can always pass a function as an argument for a function that expects a closure**, or assuming that's right: **you can always pass a function pointer as an argument type for a function that expects a closure** – 周汉成 Oct 08 '18 at 09:08
  • 2
    I tried `&Fn ..`, `&impl Fn ..` and `&dyn Fn ..` All works. So what is the difference between them? – Nawaz Oct 08 '18 at 12:36
  • 1
    @Nawaz `&Fn` is a shortcut for `&dyn Fn` which is a dynamic dispatch (lighter in memory but slower). – Denys Séguret Oct 08 '18 at 12:43
  • @DenysSéguret: And `&impl Fn` is static dispatch, I suppose? Can I use it without the `&`? Would that work? If not, **why**? – Nawaz Oct 08 '18 at 13:06
  • 2
    Taking a reference to the closure argument is pretty unusual. Normally this would be written as `fn wrap(c: impl Fn())`. – Shepmaster Oct 08 '18 at 13:12
30

There are certain cases where you can pass a closure as a function pointer. This works:

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

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

    wrap(x);
}

The important difference is that the closure is not allowed to capture anything from its environment. That means that we had to prevent the String from crossing the closure boundary.

Any closure that doesn't capture environment can be trivially rewritten as a anonymous standalone function and then converted into a function pointer.

Once you add an environment, it is no longer convertible and everything from the existing answer applies.

Note that stating -> () is non-idiomatic as that's the default if nothing is specified.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366