Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does passing a closure to function which accepts a function pointer not work?

Tags:

rust

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?

like image 502
周汉成 Avatar asked Oct 08 '18 06:10

周汉成


People also ask

Are closures function pointers?

Function pointers implement all three of the closure traits ( Fn , FnMut , and FnOnce ), so we can always pass a function pointer as an argument for a function that expects a closure.

What can not be done with function pointers?

What will we not do with function pointers? Explanation: As it is used to execute a block of code, So we will not allocate or deallocate memory.

Does rust have function pointers?

In Rust, a function pointer type, is either fn(Args...)

Can you use a pointer on an function?

You can use pointers to call functions and to pass functions as arguments to other functions. You cannot perform pointer arithmetic on pointers to functions.


2 Answers

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:

  • RFC 1558 - Closure to fn Coercion
like image 166
Shepmaster Avatar answered Oct 24 '22 02:10

Shepmaster


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
like image 27
Denys Séguret Avatar answered Oct 24 '22 02:10

Denys Séguret