目前在实现一个事件系统,事件采用保存closure的方式实现,这其中遇到一个比较棘手的问题就是如果其中一个closure用&mut方式捕捉了一个变量,那么其他closure就不可以再用任何方式捕捉这个变量(rust的borrow check系统),并且如果要&mut捕捉还需要把closure的类型写成FnMut,类似这种: add_event<F>(&mut self,f:F) where F:FnMut(...){}
目前折中的一个方法就是用Rc<RefCell<T>>
的方式了,这个就是最终用户用起来很烦,大量的变量要用这种方式包裹。
use std::rc::Rc;
use std::cell::RefCell;
enum MyCall<'a>{
Click(Box<Fn(i32)->i32 + 'a>),
Down(Box<Fn(i32,i32)->i32 + 'a>),
None,
}
struct Foo<'a>{
calls:Vec<MyCall<'a>>,
}
impl<'a> Foo<'a>{
fn new()->Foo<'a>{
Foo{
calls:Vec::new(),
}
}
fn add_click<F>(&mut self,f: F)where F:Fn(i32)->i32+'a{
self.calls.push(MyCall::Click(Box::new(f)));
}
fn do_call(&self){
if let MyCall::Click(ref c) = self.calls[0] {
println!("{}",(*c)(100));
}
if let MyCall::Click(ref c) = self.calls[1] {
println!("{}",(*c)(100));
}
}
}
#[derive(Debug)]
struct A(i32);
#[derive(Debug)]
struct Bar{
i:Rc<RefCell<A>>,
}
fn main(){
let mut b = Bar{i:Rc::new(RefCell::new(A(200)))};
let mut f = Foo::new();
f.add_click(|i|{
let bi = b.i.clone();
let mut mbi = bi.borrow_mut();
(*mbi).0 = 2000;
(*mbi).0
//b.0 = 100;
//b.0
});
f.add_click(|i|{
let bi = b.i.clone();
let mut mbi = bi.borrow_mut();
(*mbi).0 = 20000;
(*mbi).0
});
f.do_call();
println!("{:?}",b);
}
Play地址:http://is.gd/vdtWiG
输出:
2000
20000
Bar { i: RefCell { value: A(20000) } }
其实不用Rc也可以,大家可以看看这个,我目前对这个机制也不是太熟悉:
http://is.gd/78HF1Y