Reputation: 339
I am trying to create a struct like this in Rust:
pub struct Struct<T, F>
where T: Eq,
T: Hash,
F: Fn() -> T
{
hashMap: HashMap<T, F>,
value: T,
}
My constructor looks like this:
pub fn new(init_value: T) -> Struct<T, F> {
Struct {
hashMap: HashMap::new(),
value: init_state,
}
}
However when trying to instantiate the class, using let a = Struct::<MyEnum>::new(MyEnum::Init);
, the compiler complains that the generics needs two arguments (expected 2 type arguments, found 1
)
I saw here that this code works:
fn call_with_one<F>(some_closure: F) -> i32
where F: Fn(i32) -> i32 {
some_closure(1)
}
let answer = call_with_one(|x| x + 2);
I guess the problem comes from me having another generic in my template instantiation, but how can I do that?
Upvotes: 8
Views: 3904
Reputation: 65927
Struct::new
doesn't have any parameter that depends on F
, so the compiler is unable to infer what type it should use for F
. If you called a method later that used F
, then the compiler would use that information to figure out the Struct
's concrete type. For example:
use std::hash::Hash;
use std::collections::HashMap;
pub struct Struct<T, F>
where T: Eq,
T: Hash,
F: Fn() -> T,
{
hash_map: HashMap<T, F>,
value: T,
}
impl<T, F> Struct<T, F>
where T: Eq,
T: Hash,
F: Fn() -> T,
{
pub fn new(init_value: T) -> Struct<T, F> {
Struct {
hash_map: HashMap::new(),
value: init_value,
}
}
pub fn set_fn(&mut self, value: T, func: F) {
self.hash_map.insert(value, func);
}
}
fn main() {
let mut a = Struct::new(0);
a.set_fn(0, || 1); // the closure here provides the type for `F`
}
There's a problem with this though. If we call set_fn
a second time with a different closure:
fn main() {
let mut a = Struct::new(0);
a.set_fn(0, || 1);
a.set_fn(1, || 2);
}
then we get a compiler error:
error[E0308]: mismatched types
--> <anon>:33:17
|
33 | a.set_fn(1, || 2);
| ^^^^ expected closure, found a different closure
|
= note: expected type `[closure@<anon>:32:17: 32:21]`
= note: found type `[closure@<anon>:33:17: 33:21]`
note: no two closures, even if identical, have the same type
--> <anon>:33:17
|
33 | a.set_fn(1, || 2);
| ^^^^
help: consider boxing your closure and/or using it as a trait object
--> <anon>:33:17
|
33 | a.set_fn(1, || 2);
| ^^^^
As mentioned by the compiler, each closure expression defines a brand new type and evaluates to that type. However, by defining Struct
the way you did, you are forcing all functions in the HashMap
to have the same type. Is that really what you want?
If you want map different values of T
to possibly different types of closures, then you'll need to use trait objects instead of generics, as suggested by the compiler. If you want the struct to own the closure, then you'll have to use a Box
around the object type.
pub struct Struct<T>
where T: Eq,
T: Hash,
{
hash_map: HashMap<T, Box<Fn() -> T + 'static>>,
value: T,
}
set_fn
could then look like this:
pub fn set_fn<F: Fn() -> T + 'static>(&mut self, value: T, func: F) {
self.hash_map.insert(value, Box::new(func));
}
Upvotes: 11