Can't store a function in a HashSet

Hello world. I'm new to Rust (coming from Nim). Here is my code:

fn main() {
	fn test() { println!("Testing !") }
	let hm: HashMap<&str, fn()> = HashMap::from([("test", test)]);
	hm.get("test").unwrap()();
}

The compiler says: expected fn pointer, found fn item. Please what does it mean ?

Every function in rust has a unique type called a "fn item" that uniquely identifies it to the compiler. Having these special types enables some interesting optimizations but in this case you want to transform the special unique type into a normal function pointer. fn items can be coerced to function pointers, and at some call sites they are coerced automatically. Anywhere they aren't you can do an explicit cast

test as fn()

Also note that you sometimes see errors like this when the function and the function pointer type don't have compatible signatures.

1 Like

Thank you. It works.

The full error is

error[E0308]: mismatched types
 --> src/main.rs:5:32
  |
5 |     let hm: HashMap<&str, fn()> = HashMap::from([("test", test)]);
  |             -------------------   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found fn item
  |             |
  |             expected due to this
  |
  = note: expected struct `HashMap<&str, fn()>`
             found struct `HashMap<&str, fn() {test}>`

which isn't that much more enlightening without knowing what a "fn item" is, but at least includes the test name to indicate that fn() {test} is tied to the test function somehow, whereas fn() isn't.

I wonder if it'd be possible for the diagnostic to suggest using as fn() when encountering a mismatch between fn pointer and fn item with the same signature. It might not be possible to locate the source fn item expression span in the general case, but adding a spanless = help: try converting to fn pointer explicitly with `test as fn()` should always be possible. The name is knowable from the fn item type, and though the fn item may be coming from a different binding, the fn item was named by name somewhere, so it's always an accurate hint, even if sometimes suboptimal (e.g. changing the type elsewhere which does want the fn item instead of fn pointer).

I'm away from any devices where it'd be convenient to file the issue, otherwise I'd do it myself. archnim: the Rust project considers suboptimal diagnostics a bug; if you're willing, we'd love if you could file a diagnostics bug report with a standalone reproducer (exactly like in the OP), the full diagnostic, and what extra information and/or wording would have helped you diagnose the error (e.g. a suggestion to try an as fn() cast). It's super easy for experienced developers to understand what a diagnostic is saying, so experience reports of sharp edges from newer users not yet well versed in the language of the compiler are always appreciated. At absolute worst, it's triaged and closed as a duplicate of an existing report, but it still serves as a new data point that it's an edge worth sanding, and that's still valuable.

Plus, help items added to guide less experienced developers can still be helpful to more experienced developers! The difference between a fn item and fn pointer can take a bit to parse, whereas a fix suggestion (where possible) or even just "help: try as fn()" can be more easily scanned.

TL;DR: don't be afraid to ask questions when it's not clear what the compiler is trying to tell you. The community likes to help, and to teach the compiler to directly provide more of that help in the future.

5 Likes