Puzzling "expected fn pointer, found fn item"

I can't understand what's happening here. I reduced the issue to this minimal example:

fn main() {
    test(&[f1, f2].to_vec()); // It works when storing different functions
    test(&[f1, f1].to_vec()); // <<< It doesn't work when storing only one function
}

fn f1(_x: u8) {}
fn f2(_x: u8) {}

pub fn test(_v: &Vec<fn(u8)>) {}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
 --> src/main.rs:3:10
  |
3 |     test(&[f1, f1].to_vec()); // <<< It doesn't work when storing only one function
  |          ^^^^^^^^^^^^^^^^^^ expected fn pointer, found fn item
  |
  = note: expected reference `&std::vec::Vec<fn(u8)>`
             found reference `&std::vec::Vec<fn(u8) {f1}>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

A function item is a special zero-sized type tied to exactly one function in your program. This is useful because it compiles down to just hard-coding the correct function when the function item is used, which is more efficient than calling a dynamic function pointer.

In some cases, the compiler will automatically cast function items to function pointers. You are hitting one of those cases in the first test, but not the latter.

Note that you never want &Vec<T> unless you specifically need to call the capacity() function. Prefer a slice type &[T].

1 Like

Ok thank you. So in order to have a function that processes a chain of functions, ranging from 1 to n... What would be the rusty thing to do there so it doesn't fail when there is only 1 type of function? Can I cast it to the expected type?

Ok, I converted it to a slice and it seems it solves this particular problem, at least in the example. I'll try it out in the bigger program. Thank you again

Nope. I'm still facing the same mismatching problem, now between arrays and slices. I've tried different ways to coerce it but can't figure it out.

This is the updated example:

fn main() {
    let c1 = [f1, f2];
    test(&c1);
    
    let c2 = [f1, f1];
    test(&c2); // ERROR: expected slice, found array of 2 elements
}

fn f1(_x: u8) {}
fn f2(_x: u8) {}

pub fn test(_v: &[fn(u8)]) {}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
 --> src/main.rs:6:10
  |
6 |     test(c2); // ERROR: expected slice, found array of 2 elements
  |          ^^ expected slice, found array of 2 elements 
  |
  = note: expected reference `&[fn(u8)]`
             found reference `&[fn(u8) {f1}; 2]`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

You can explicitly convert one of the function items to a function pointer if it doesn't happen automatically.

let c2 = [f1 as fn(u8), f1];
1 Like

Or, in your specific example, you can also specify the type of c2

let c2: [fn(u8); 2] = [f1, f1];

or pass the value directly so that the type-checker sees the expected type (because it knows the type of test)

test(&[f1, f1]);
2 Likes

Thank you both so much. So obvious in retrospective.I like to see I had three options of which

is my favourite, since I already tried that, but without specifying its size. Unfortunately the compiler error didn't suggested me to do that. EDIT: In the compiler msg was the answer, sorry.

note: expected slice `[fn(u8)]`
             found array `[fn(u8); 2]`
1 Like

I want to end up sharing a more complete example with the addition of type aliases. In this case I found that having extra aliases for the sized Arrays feels the most comfortable.

use ndarray::prelude::*;

pub type A1 = Array1<f64>;
pub type ArrayFunction = fn(&A1) -> A1;
pub type Chain = [ArrayFunction];
pub type Chain1 = [ArrayFunction;1]; // for convenience
pub type Chain2 = [ArrayFunction;2]; //  "      "

fn main() {
    let c1 = [f1, f2]; // OK
    test(&c1);
    
    let c2:Chain2 = [f1, f1]; // OK only by specifying the type
    test(&c2);
}

fn f1(x: &A1) -> A1 { x.to_owned() }
fn f2(x: &A1) -> A1 { x.to_owned() }

pub fn test(_v: &Chain) {}

If you are looking for code ergonomics, some very simple macros can come in handy:

macro_rules! Chain {
    [..] => ([ArrayFunction]);
    [$N:expr] => ([ArrayFunction; $N]);
}

// ...
    let c2: Chain![2] = [f1, f1];
// ...

fn test (_: &Chain![..])

or, use a macro to coerce at construction site directly:

macro_rules! chain { [ $($f:expr),* $(,)? ] => (
    [
        $( $f as ArrayFunction, )*
    ]
)}

type A1 = Array1<f64>;
type ArrayFunction = fn(&'_ A1) -> A1;
type Chain = [ArrayFunction];

fn main ()
{
    let c1 = chain![f1, f2]; // OK
    test(&c1);
    
    let c2 = chain![f1, f1]; // OK
    test(&c2);
}

fn f1 (x: &'_ A1) -> A1 { x.to_owned() }
fn f2 (x: &'_ A1) -> A1 { x.to_owned() }

pub
fn test (_: &'_ Chain)
{}
2 Likes

Oh wow, that's great, thank you. I love the second example.

I haven't got to learn macros yet, I don't know why I keep delaying it. I think I'm gonna start today.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.