Reputation: 3360
The following code shows some ways of creating a HashMap.
use std::collections::HashMap;
fn main() {
let h1 : HashMap<usize, (fn(usize) -> usize, usize, usize)> = HashMap::new();
h1.insert(1, (|n| n, 1, 1)); // works fine
let h2 : HashMap<usize, (fn(usize) -> usize, usize, usize)> = HashMap::from([(1, (|n| n, 1, 1))]); // error
HashMap::<usize, (fn(usize) -> usize, usize, usize)>::from([(1, (|n| n, 1, 1))]); // error
}
These seems to essentially express the same thing, but the Rust compiler disagrees (playground)!
error[E0308]: mismatched types
--> src/main.rs:23:67
|
23 | let h2 : HashMap<usize, (fn(usize) -> usize, usize, usize)> = HashMap::from([(1, (|n| n, 1, 1))]);
| -------------------------------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found closure
| |
| expected due to this
|
= note: expected struct `HashMap<usize, (fn(usize) -> usize, usize, usize)>`
found struct `HashMap<{integer}, ([closure@src/main.rs:23:87: 23:92], {integer}, {integer})>`
error[E0277]: the trait bound `HashMap<usize, (fn(usize) -> usize, usize, usize)>: From<[({integer}, ([closure@src/main.rs:24:70: 24:75], {integer}, {integer})); 1]>` is not satisfied
--> src/main.rs:24:5
|
24 | HashMap::<usize, (fn(usize) -> usize, usize, usize)>::from([(1, (|n| n, 1, 1))]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<[({integer}, ([closure@src/main.rs:24:70: 24:75], {integer}, {integer})); 1]>` is not implemented for `HashMap<usize, (fn(usize) -> usize, usize, usize)>`
|
= help: the following implementations were found:
<HashMap<K, V> as From<[(K, V); N]>>
note: required by `from`
Why do I get these errors for way 2 and 3, but not for the first way?
Upvotes: 2
Views: 163
Reputation: 1263
fn(usize) -> usize
is a function pointer and |n| n
is a closure. Closures that don't capture environment variables can be automatically converted to function pointers, which happens in this line
h1.insert(1, (|n| n, 1, 1));
More information about the distinction between function pointers and closures can be found in this question and answer
For some reason, the automatic conversion is not applied in the second and third cases but the compiler can be coerced into doing so by typing (|n| n) as _
or (|n| n) as fn(usize) -> usize
.
If you want to store closures that capture variables (instead of function pointers), you have to put them in a Box
similar to this:
let some = "hello".to_string();
let mut h1 : HashMap<usize, Box<dyn Fn(usize) -> usize>> = HashMap::new();
h1.insert(1, Box::new(move |_n| some.len())); // 'some' is moved here
Upvotes: 1