Problem with function signatures

When compiling the following code:

	let (get_u16, get_u32) = match result.endianess {
		Endianess::Little => (u16::from_le_bytes, u32::from_le_bytes ),
		Endianess::Big    => (u16::from_be_bytes, u32::from_be_bytes ),
	};

The compiler complains that th functions signatures are different in both arms of the match expression, even though, according to documentation, both from_le_bytes and from_be_bytes have the signature fn([u8; _]) -> u32. The error it displays is the following:

error[E0308]: match arms have incompatible types
  --> src/dbg/internal/elf/info.rs:85:25
   |
83 |           let (get_u16, get_u32) = match result.endianess {
   |  __________________________________-
84 | |             Endianess::Little => (u16::from_le_bytes, u32::from_le_bytes ),
   | |                                  ----------------------------------------- this is found to be of type `(fn([u8; _]) -> u16 {core::num::<impl u16>::from_le_bytes}, fn([u8; _]) -> u32 {core::num::<impl u32>::from_le_bytes})`
85 | |             Endianess::Big    => (u16::from_be_bytes, u32::from_be_bytes ),
   | |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found a different fn item
86 | |         };
   | |_________- `match` arms have incompatible types
   |
   = note: expected type `(fn([u8; _]) -> u16 {core::num::<impl u16>::from_le_bytes}, fn([u8; _]) -> u32 {core::num::<impl u32>::from_le_bytes})`
              found type `(fn([u8; _]) -> u16 {core::num::<impl u16>::from_be_bytes}, fn([u8; _]) -> u32 {core::num::<impl u32>::from_be_bytes})`

I don't know if this is a bug, desired behavior or simply the compiler being picky. Any ideas on how to solve this?

Function items have their own unique types - to get this to work, you need to explicitly state that you want to work with function pointers so that the types get coerced properly.

// These aliases aren't nececcary, just makes the `let` line a bit nicer 
// to look at!
type FuncA = fn([u8; 2]) -> u16;
type FuncB = fn([u8; 4]) -> u32;

let (get_u16, get_u32): (FuncA, FuncB) = match result.endianess {
    Endianess::Little => (u16::from_le_bytes, u32::from_le_bytes),
    Endianess::Big => (u16::from_be_bytes, u32::from_be_bytes),
};

You could also cast the function items with as.

See this StackOverflow post for more info.

5 Likes

Thanks for that. What I found weird was that when doing it one by one, the compiler correctly assumed that I wanted to work with function pointers. With type anotation it works like a charm.