16

How do you stick functions (or function pointers) into an array for testing purposes?

fn foo() -> isize { 1 }
fn bar() -> isize { 2 }

fn main() {
    let functions = vec![foo, bar];
    println!("foo() = {}, bar() = {}", functions[0](), functions[1]());
}

This code in the Rust playground

This is the error code I get:

error: mismatched types:
 expected `fn() -> isize {foo}`,
    found `fn() -> isize {bar}`
(expected fn item,
    found a different fn item) [E0308]

    let functions = vec![foo, bar];
                              ^~~

Rust is treating my functions (values) as different types despite having the same signatures, which I find surprising.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Andrew Wagner
  • 22,677
  • 21
  • 86
  • 100
  • Curiously, this is only a limitation of `vec![]` (actually, [it's a problem with `box [...]`](https://stackoverflow.com/questions/54632524/why-does-boxing-an-array-of-function-pointers-with-box-syntax-only-work-with-a)). You can easily create an array or slice of function pointers: `let arr = [foo, bar]`. – Lukas Kalbertodt Feb 12 '19 at 03:48

1 Answers1

17

At some point recently, each function was given its own, distinct type for... reasons that I don't recall. Upshot is that you need to give the compiler a hint (note the type on functions):

fn foo() -> isize {
    1
}
fn bar() -> isize {
    2
}
fn main() {
    let functions: Vec<fn() -> isize> = vec![foo, bar];
    println!("foo() = {}, bar() = {}", functions[0](), functions[1]());
}

You can also do this like so:

let functions = vec![foo as fn() -> isize, bar];
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
DK.
  • 55,277
  • 5
  • 189
  • 162
  • Thanks! It's always a shame to see a use case get harder; I hope they had some reason for this breaking change. Annotating the type on the left hand side isn't so bad ~if you know how to write function type signatures, which I did not yet. – Andrew Wagner Jan 26 '15 at 14:04
  • "fn items" have a benefit: It avoids a call through a function pointer which is a virtual call. LLVM may *devirtualize* that call in an optimization pass, but fn item and unboxed closure calls should always be direct, without relying on the optimizer to get it right. – bluss Jan 26 '15 at 20:47
  • You can place them in an array, but you have to have a bit more casting: `let functions = [foo as fn() -> isize, bar as fn() -> isize];` or even `let functions: [fn() -> isize; 2] = [foo, bar];`. You could also make a slice `let functions: &[fn() -> isize] = &[foo, bar];`. – Shepmaster May 05 '16 at 23:51
  • I wish I could upvote this more than once. Without inheritance in the language I completely forgot about the idea that it might be inferring too specific a type, and need to be reined in. Spent several frustrated hours on this.... – Richard Rast Aug 09 '17 at 12:27