使用闭包创建回调系统

Mar*_*vel 7 closures rust

我正在尝试制作类似"回调系统"的东西.例如,有一个窗口和几个按钮.该窗口为每个按钮设置回调.两个回调都应该改变窗口的状态.编译器不允许&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();
}
Run Code Online (Sandbox Code Playgroud)

She*_*ter 6

您将遇到此行的直接问题:

self.btn.set(|| self.hi());
Run Code Online (Sandbox Code Playgroud)

在这里,你需要借用self可变性来修改btn.你试图self在闭包中借用可变性.这将立即遇到问题,因为Rust不允许您对同一对象进行多次可变引用(称为别名).这是该语言的内存安全保证的基本部分.

此外,从概念上讲,您正在尝试建立一个引用循环 - Window了解ButtonButton了解Window.虽然这是可能的,但通常不是你想要的.一旦参考文献有一个循环,就很难解开它们.您还可以搜索有关在Rust中创建图形的其他问题(而不是树木),以查看其他人遇到的类似问题.

理想情况下,您可以将代码构造为树.在这里,我选择了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();
}
Run Code Online (Sandbox Code Playgroud)

  • 其实我可以。将单击事件发布到队列并根据需要在主循环中处理它们。[完整示例](http://is.gd/X39182) (2认同)