使用闭包创建回调系统
Creating a callback system using closures
我正在尝试制作类似 "callback system" 的东西。例如,其中有一个 window 和几个按钮。 window 为每个按钮设置回调。两个回调都应更改 window 的状态。编译器不允许在我的闭包/回调中捕获 &self
,我不知道如何让它工作。
我应该遵循哪些常见的回调模式?
这是一个简单的示例,因为所有组件都具有相同的生命周期。如果组件的生命周期不同怎么办?
struct Button<'a> {
f: Option<Box<Fn() + 'a>>,
}
impl<'a> Button<'a> {
fn new() -> Button<'a> { Button { f: None } }
fn set<T: Fn() + 'a>(&mut self, f: T) { self.f = Some(Box::new(f)); }
fn unset(&mut self) { self.f = None; }
fn call(&self) { match self.f { Some(ref f) => f(), None => () } }
}
struct Window<'a> {
btn: Button<'a>,
//btns: Vec<Button<'a>>,
}
impl<'a> Window<'a> {
fn new() -> Window<'a> {
Window { btn: Button::new() }
}
fn hi(&mut self) { // self is mutable
println!("callback");
}
fn run(&mut self) {
// Compile error: cannot infer an appropriate lifetime for
// capture of `self` by closure due to conflicting requirements
self.btn.set(|| self.hi()); // How to make it work?
self.btn.call();
self.btn.unset();
}
}
fn main() {
let mut wnd = Window::new();
wnd.run();
}
您将 运行 立即遇到此行的问题:
self.btn.set(|| self.hi());
这里需要借用self
作为mutable,才能修改btn
。您 也 试图借用 self
作为闭包中的可变对象。这将立即 运行 出现问题,因为 Rust 不允许您对同一对象有多个可变引用(称为 别名 )。这是语言内存安全保证的基本部分。
此外,从概念上讲,您正在尝试建立一个引用循环 - Window
知道 Button
而 Button
知道 Window
。虽然这是可能的,但它通常不是您想要的。一旦引用有一个循环,就很难将它们分开。您还可以搜索其他询问有关在 Rust 中创建 graphs(而不是 trees)的问题,以查看其他人遇到的类似问题。
理想情况下,您可以将代码结构化为一棵树。这里,我选择Button
可以知道Window
,反之则不行:
struct Button<'a> {
f: Option<Box<FnMut() + 'a>>,
}
impl<'a> Button<'a> {
fn new() -> Button<'a> { Button { f: None } }
fn set<T: FnMut() + 'a>(&mut self, f: T) { self.f = Some(Box::new(f)); }
fn unset(&mut self) { self.f = None; }
fn call(&mut self) { match self.f { Some(ref mut f) => f(), None => () } }
}
struct Window;
impl Window {
fn hi(&mut self) {
println!("callback");
}
}
fn main() {
let mut wnd = Window;
let mut btn = Button::new();
btn.set(|| wnd.hi());
btn.call();
btn.unset();
}
我正在尝试制作类似 "callback system" 的东西。例如,其中有一个 window 和几个按钮。 window 为每个按钮设置回调。两个回调都应更改 window 的状态。编译器不允许在我的闭包/回调中捕获 &self
,我不知道如何让它工作。
我应该遵循哪些常见的回调模式?
这是一个简单的示例,因为所有组件都具有相同的生命周期。如果组件的生命周期不同怎么办?
struct Button<'a> {
f: Option<Box<Fn() + 'a>>,
}
impl<'a> Button<'a> {
fn new() -> Button<'a> { Button { f: None } }
fn set<T: Fn() + 'a>(&mut self, f: T) { self.f = Some(Box::new(f)); }
fn unset(&mut self) { self.f = None; }
fn call(&self) { match self.f { Some(ref f) => f(), None => () } }
}
struct Window<'a> {
btn: Button<'a>,
//btns: Vec<Button<'a>>,
}
impl<'a> Window<'a> {
fn new() -> Window<'a> {
Window { btn: Button::new() }
}
fn hi(&mut self) { // self is mutable
println!("callback");
}
fn run(&mut self) {
// Compile error: cannot infer an appropriate lifetime for
// capture of `self` by closure due to conflicting requirements
self.btn.set(|| self.hi()); // How to make it work?
self.btn.call();
self.btn.unset();
}
}
fn main() {
let mut wnd = Window::new();
wnd.run();
}
您将 运行 立即遇到此行的问题:
self.btn.set(|| self.hi());
这里需要借用self
作为mutable,才能修改btn
。您 也 试图借用 self
作为闭包中的可变对象。这将立即 运行 出现问题,因为 Rust 不允许您对同一对象有多个可变引用(称为 别名 )。这是语言内存安全保证的基本部分。
此外,从概念上讲,您正在尝试建立一个引用循环 - Window
知道 Button
而 Button
知道 Window
。虽然这是可能的,但它通常不是您想要的。一旦引用有一个循环,就很难将它们分开。您还可以搜索其他询问有关在 Rust 中创建 graphs(而不是 trees)的问题,以查看其他人遇到的类似问题。
理想情况下,您可以将代码结构化为一棵树。这里,我选择Button
可以知道Window
,反之则不行:
struct Button<'a> {
f: Option<Box<FnMut() + 'a>>,
}
impl<'a> Button<'a> {
fn new() -> Button<'a> { Button { f: None } }
fn set<T: FnMut() + 'a>(&mut self, f: T) { self.f = Some(Box::new(f)); }
fn unset(&mut self) { self.f = None; }
fn call(&mut self) { match self.f { Some(ref mut f) => f(), None => () } }
}
struct Window;
impl Window {
fn hi(&mut self) {
println!("callback");
}
}
fn main() {
let mut wnd = Window;
let mut btn = Button::new();
btn.set(|| wnd.hi());
btn.call();
btn.unset();
}