使用闭包创建回调系统 [英] Creating a callback system using closures

查看:98
本文介绍了使用闭包创建回调系统的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想做一个像回调系统。例如,有一个窗口和几个按钮。该窗口设置每个按钮的回调。这两个回调都应该改变窗口的状态。编译器不允许捕获& self 在我的闭包/回调,我不知道如何使它的工作。

I'm trying to make something like a "callback system". For example, there is a window and a couple of buttons in it. The window sets callbacks for each button. Both callbacks should change the state of the window. The compiler doesn't allow capturing &self in my closures / callbacks, and I don't know how to make it work.

我应该跟踪的回调有什么常见模式吗?

Are there any common patterns for callbacks I should be following?

这是一个简单的例子,具有相同的寿命。如果组件有不同的生命周期,该怎么办?

This is an easy example as all components have the same lifetime. What if the components have different lifetimes?

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不允许有多个可变引用到同一个对象(称为别名)。这是语言的内存安全保证的一个基本部分。

Here, you need to borrow self as mutable in order to modify btn. You are also trying to borrow self as mutable in the closure. This is going to immediately run into problems because Rust does not allow you to have multiple mutable references to the same object (known as aliasing). This is a fundamental part of the the memory-safety guarantees of the language.

此外,从概念上讲,你正在尝试建立一个引用循环 - Window 知道按钮按钮 。虽然这是可能的,但它通常不是你想要的。一旦引用有一个周期,很难解开它们。您也可以在Rust(而不是)中搜索关于创建图表的其他问题,以查看其他人遇到的类似问题。

Also, conceptually you are trying to set up a cycle of references - the Window knows about Button and Button knows about Window. While this is possible, it often isn't what you want. Once the references have a cycle, it's very hard to disentangle them. You can also search other questions that ask about creating graphs in Rust (as opposed to trees) to see similar issues other people have had.

理想情况下,您可以将代码结构化为树。在这里,我选择 Button 可以了解窗口,反之亦然:

Ideally, you can structure your code as a tree. Here, I chose that Button can know about Window, but not vice versa:

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();
}

这篇关于使用闭包创建回调系统的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆