How could I achieve something like this in Rust
struct TestStruct {
map:HashMap<String, Box<FnMut(i32) -> ()>>,
val:i32
}
impl TestStruct {
fn new() -> Self {
let mut ts = TestStruct{ map: Default::default(), val: 0 };
ts.map.insert(String::from("add"),Box::new(|a| ts.val+=a ));
ts.map.insert(String::from("mult"),Box::new(|a| ts.val*=a ));
ts
}
fn execute(&mut self, cmd:&str,arg:i32) {
let f = self.map.get_mut(cmd).unwrap();
f(arg);
}
}
This obviously doesn't work since ts
is mutably borrowed multiple times
Is the solution really this ugly?
impl TestStruct {
fn new() -> Self {
let mut map:HashMap<String, Box<Fn(i32) -> ()>> = HashMap::new();
let val = Rc::new(RefCell::new(0));
let v1 = val.clone();
map.insert(String::from("add"),Box::new(move |a|
{
let mut mutator = v1.borrow_mut();
*mutator+=a;
}
));
let v1 = val.clone();
map.insert(String::from("mult"),Box::new(move |a| {
{
let mut mutator = v1.borrow_mut();
*mutator*=a;
}
}
));
TestStruct{ map, val }
}
fn execute(&mut self, cmd:&str,arg:i32) {
let f = self.map.get_mut(cmd).unwrap();
f(arg);
}
}
Is there a way to achieve something like this with a completely different approach?
For completeness I am including the Rc version:
struct TestStruct {
map:HashMap<String, Box<Fn(i32) -> ()>>,
val:Rc<Cell<i32>>
}
impl TestStruct {
fn new() -> Self {
let mut map:HashMap<String, Box<Fn(i32) -> ()>> = HashMap::new();
let val = Rc::new(Cell::new(0));
let v1 = val.clone();
map.insert(String::from("add"),Box::new(move |a| v1.set(v1.get()+a)));
let v1 = val.clone();
map.insert(String::from("mult"),Box::new(move |a| v1.set(v1.get()*a)));
TestStruct{ map, val }
}
fn execute(&mut self, cmd:&str,arg:i32) {
let f = self.map.get_mut(cmd).unwrap();
f(arg);
}
}
I would recommend avoiding shared mutability unless absolutely necessary. In this case you can give a mutable reference to your functions only when they are executing by passing val
as a mutable reference parameter:
use std::collections::HashMap;
struct TestStruct {
map: HashMap<String, Box<dyn FnMut(&mut i32, i32) -> ()>>,
val: i32
}
impl TestStruct {
fn new() -> Self {
let mut ts = TestStruct { map: Default::default(), val: 0 };
ts.map.insert(String::from("add"), Box::new(|val, a| *val += a ));
ts.map.insert(String::from("mult"), Box::new(|val, a| *val *= a ));
ts
}
fn execute(&mut self, cmd: &str, arg: i32) {
let f = self.map.get_mut(cmd).unwrap();
f(&mut self.val, arg);
}
}