提问人:Jaka 提问时间:9/30/2023 最后编辑:Jaka 更新时间:10/1/2023 访问量:66
结构成员 (HashMap) 中的闭包,用于捕获对另一个结构成员的可变引用
Closures in a struct member (HashMap) which capture a mutable reference to another struct member
问:
我怎么能在 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);
}
}
这显然是行不通的,因为可以多次可变地借用ts
解决方案真的这么丑陋吗?
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);
}
}
有没有办法用完全不同的方法实现这样的事情?
为了完整起见,我包括 Rc 版本:
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);
}
}
答:
2赞
kmdreko
9/30/2023
#1
除非绝对必要,否则我建议避免共享可变性。在这种情况下,只有在函数执行时,您才能通过作为可变引用参数传递来为函数提供可变引用:val
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);
}
}
评论
0赞
Jaka
10/1/2023
此外,由于使用解决方案时,闭包不需要捕获任何内容,因此可以更改映射签名,因为闭包可以成为简单的函数指针。map: HashMap<String, fn(&mut i32, i32) -> ()>
评论
ts
TestStruct::new
Rc<Cell>
val
execute
Cell
RefCell
RefCell
Sync