Reputation: 3567
In relation to this question, Storing a closure in a HashMap, I learned that properly passing closures to a function requires the function to be generic and take any type that implements the Fn, FnMut, or FnOnce trait.
In implementing part of a library from C++ as a learning exercise, I require some type abstraction kind of like this.
use std::collections::HashMap;
struct Event;
trait IObject {
fn makeFunc<F : FnMut(&Event)>(&mut self, s : &str, f : F);
}
struct Object1<'a> {
m_funcs : HashMap<String, Box<FnMut(&Event) + 'a>>
}
impl <'a> Object1<'a> {
fn new() -> Object1<'a> {
Object1 {m_funcs : HashMap::new()}
}
}
impl <'a> IObject for Object1<'a> {
fn makeFunc<F : FnMut(&Event) + 'a>(&mut self, s: &str, f: F) {
self.m_funcs.insert(String::from_str(s), Box::new(f));
}
}
fn main() {
let obj : &IObject = &Object1::new();
println!("Hello, world!");
}
However, the error returned says that IObject cannot be a trait object because it contains a method with generic parameters. However, to pass a closure to a function at all, I require generics. Can someone show me how to achieve the abstraction I'm looking for while still being able to pass closures to functions?
Upvotes: 5
Views: 1695
Reputation: 90852
You cannot get around this; static and dynamic dispatch do not mix. The monomorphisation that static dispatch (generics) does simply cannot work with the vtable used in dynamic dispatch (trait objects).
One of the two will have to go: either the usage of IObject
as a trait object, or the generic function argument, in favour of accepting a Box<FnMut(&Event) + 'a>
.
By the way, note how your IObject
implementation is not matching the trait—the trait gives no lifetime bound on F
, where your implementation does. You’d need to add 'a
as a generic on the trait definition anyway (generic lifetimes are OK with trait objects).
Upvotes: 6