关闭可能会超过当前功能 [英] Closure may outlive the current function

查看:66
本文介绍了关闭可能会超过当前功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我才刚刚开始学习Rust.为此目的,我正在Rust中重写我的C ++项目,但是最大的问题是闭包之类的生命周期.

I am just starting to learn Rust. For this purpose I am rewriting my C++ project in Rust, but the biggest problems are lifetimes of closures and such.

我在这里及以下:

use std::sync::Arc;
use std::cell::{RefCell, Cell};

struct Context {
    handler: RefCell<Option<Arc<Handler>>>,
}

impl Context {
    pub fn new() -> Arc<Context> {
        let context = Arc::new(Context{
            handler: RefCell::new(None),
        });

        let handler = Handler::new(context.clone());

        (*context.handler.borrow_mut()) = Some(handler);

        context
    }

    pub fn get_handler(&self) -> Arc<Handler> {
        self.handler.borrow().as_ref().unwrap().clone()
    }
}

struct Handler {
    context: Arc<Context>,

    clickables: RefCell<Vec<Arc<Clickable>>>,
}

impl Handler {
    pub fn new(context: Arc<Context>) -> Arc<Handler> {
        Arc::new(Handler{
            context: context,

            clickables: RefCell::new(Vec::new()),
        })
    }

    pub fn add_clickable(&self, clickable: Arc<Clickable>) {
        self.clickables.borrow_mut().push(clickable);
    }

    pub fn remove_clickable(&self, clickable: Arc<Clickable>) {
        // remove stuff ...
    }
}

struct Clickable {
    context: Arc<Context>,

    callback: RefCell<Option<Box<Fn()>>>,
}

impl Clickable {
    pub fn new(context: Arc<Context>) -> Arc<Clickable> {
        let clickable = Arc::new(Clickable{
            context: context.clone(),

            callback: RefCell::new(None),
        });

        context.get_handler().add_clickable(clickable.clone());

        clickable
    }

    pub fn remove(clickable: Arc<Clickable>) {
        clickable.context.get_handler().remove_clickable(clickable);
    }

    pub fn set_callback(&self, callback: Option<Box<Fn()>>) {
        (*self.callback.borrow_mut()) = callback;
    }

    pub fn click(&self) {
        match *self.callback.borrow() {
            Some(ref callback) => (callback)(),
            None => (),
        }
    }
}

struct Button {
    context: Arc<Context>,

    clickable: Arc<Clickable>,
}

impl Button {
    pub fn new(context: Arc<Context>) -> Arc<Button> {
        let clickable = Clickable::new(context.clone());

        let button = Arc::new(Button{
            context: context,

            clickable: clickable.clone(),
        });

        let tmp_callback = Box::new(|| {
            button.do_stuff();
        });
        clickable.set_callback(Some(tmp_callback));

        button
    }

    pub fn do_stuff(&self) {
        // doing crazy stuff
        let mut i = 0;

        for j in 0..100 {
            i = j*i;
        }
    }

    pub fn click(&self) {
        self.clickable.click();
    }
}

impl Drop for Button {
    fn drop(&mut self) {
        Clickable::remove(self.clickable.clone());
    }
}

fn main() {
    let context = Context::new();

    let button = Button::new(context.clone());

    button.click();
}

我只是不知道如何在闭包中传递引用.

I just don't know how to pass references in closures.

另一个丑陋的事情是我的HandlerContext需要彼此.有没有更好的方法来创建此依赖关系?

Another ugly thing is that my Handler and my Context need each other. Is there a nicer way to to create this dependency?

推荐答案

关闭初始代码

pub fn new(context: Arc<Context>) -> Arc<Button> {
    let clickable = Clickable::new(context.clone());

    let button = Arc::new(Button{
        context: context,

        clickable: clickable.clone(),
    });

    let tmp_callback = Box::new(|| {
        button.do_stuff();
    });
    clickable.set_callback(Some(tmp_callback));

    button
}

首先,让我们注意您遇到的错误

First off, let's note the error you're getting

    error[E0373]: closure may outlive the current function, but it borrows `button`, which is owned by the current function
   --> src/main.rs:101:37
    |
101 |         let tmp_callback = Box::new(|| {
    |                                     ^^ may outlive borrowed value `button`
102 |             button.do_stuff();
    |             ------ `button` is borrowed here
    |
help: to force the closure to take ownership of `button` (and any other referenced variables), use the `move` keyword, as shown:
    |         let tmp_callback = Box::new(move || {

请注意底部的help块,您需要使用move闭包,因为当new函数结束时,堆栈上的button变量将超出范围.避免这种情况的唯一方法是将其所有权移到回调本身.因此,您会改变

Noting the help block at the bottom, you need to use a move closure, because when the new function ends, the button variable on the stack will go out of scope. The only way to avoid that is to move ownership of it to the callback itself. Thus you'd change

let tmp_callback = Box::new(|| {

let tmp_callback = Box::new(move || {

现在,您会收到第二个错误:

Now, you'd get a second error:

    error[E0382]: use of moved value: `button`
   --> src/main.rs:107:9
    |
102 |         let tmp_callback = Box::new(move || {
    |                                     ------- value moved (into closure) here
...
107 |         button
    |         ^^^^^^ value used here after move
    |
    = note: move occurs because `button` has type `std::sync::Arc<Button>`, which does not implement the `Copy` trait

这里的错误可能会更清楚一些.您正在尝试将button值的所有权移到回调闭包中,但是new函数的主体内使用它,当您将其返回时,试图拥有价值的两种不同方式.

And the error here may be a little clearer. You're trying to move ownership of the button value into the callback closure, but you also use it inside the body of the new function when you return it, and you can't have two different things trying to own the value.

解决方案有望达到您的预期.您必须制作一个副本,您可以可以拥有该副本.然后,您需要更改

The solution to that is hopefully what you'd guess. You have to make a copy that you can take ownership of. You'll want to then change

let tmp_callback = Box::new(move || {
    button.do_stuff();

let button_clone = button.clone();
let tmp_callback = Box::new(move || {
    button_clone.do_stuff();

现在,您已经创建了一个新的Button对象,并为该对象本身返回了Arc,同时还为回调本身提供了第二个Arc的所有权.

Now you've created a new Button object, and returned an Arc for the object itself, while also giving ownership of a second Arc to the callback itself.

给出您的评论,这里确实存在一个循环依赖的问题,因为您的Clickable对象持有对Button的引用的所有权,而Button持有对Clickable的引用的所有权.解决此问题的最简单方法是第三次更新该代码,

Given your comment, there is indeed an issue here of cyclic dependencies, since your Clickable object holds ownership of a reference to Button, while Button holds ownership of a reference to Clickable. The easiest way to fix this here would be to update that code a third time, from

let button_clone = button.clone();
let tmp_callback = Box::new(move || {
    button_clone.do_stuff();

let button_weak = Arc::downgrade(&button);
let tmp_callback = Box::new(move || {
    if let Some(button) = button_weak.upgrade() {
        button.do_stuff();
    }
});

,因此Clickable将仅保留对Button的弱引用,并且如果不再引用Button,则回调将为空操作.

so the Clickable will only hold a weak reference to the Button, and if the Button is no longer referenced, the callback will be a no-op.

您可能还想考虑将clickables列表作为Weak引用而不是强引用,因此可以在删除引用的项目时从其中删除项目.

You'd also probably want to consider making clickables a list of Weak references instead of strong references, so you can remove items from it when the item they reference is removed.

这篇关于关闭可能会超过当前功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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