Rust 闭包可能比当前函数更有效

Rust 闭包可能比当前函数更有效,rust,closures,lifetime,Rust,Closures,Lifetime,我刚开始学生锈。为了这个目的,我在RIST中重写我的C++项目,但是最大的问题是关闭的生命周期等等。p> 我为我的问题创建了一个绝对最小的场景,如下所示: use std::sync::Arc; use std::cell::{RefCell, Cell}; struct Context { handler: RefCell<Option<Arc<Handler>>>, } impl Context { pub fn new() ->

我刚开始学生锈。为了这个目的,我在RIST中重写我的C++项目,但是最大的问题是关闭的生命周期等等。p> 我为我的问题创建了一个绝对最小的场景,如下所示:

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();
}
使用std::sync::Arc;
使用std::cell::{RefCell,cell};
结构上下文{
处理程序:RefCell,
}
impl上下文{
pub fn new()->Arc{
let context=Arc::new(上下文{
处理程序:RefCell::new(无),
});
让handler=handler::new(context.clone());
(*context.handler.borrow_mut())=Some(handler);
上下文
}
pub fn get_handler(&self)->Arc{
self.handler.borrow().as_ref().unwrap().clone()
}
}
结构处理程序{
背景:Arc,
可单击项:RefCell,
}
impl处理器{
新发布(上下文:Arc)->Arc{
Arc::新的(处理器){
上下文:上下文,
可单击项:RefCell::new(Vec::new()),
})
}
发布fn添加可单击(&self,可单击:弧){
self.clickables.borrow_mut().push(可点击);
}
发布fn删除\可单击(&self,可单击:弧){
//删除内容。。。
}
}
结构可点击{
背景:Arc,
回调:RefCell,
}
简单可点击{
新发布(上下文:Arc)->Arc{
让可点击=弧::新建(可点击{
context:context.clone(),
回调:RefCell::new(无),
});
context.get_handler().add_clickable(clickable.clone());
可点击
}
发布fn删除(可单击:Arc){
clickable.context.get_handler().remove_clickable(clickable);
}
pub fn set_回调(&self,回调:选项){
(*self.callback.borrow_mut())=回调;
}
发布fn单击(&self){
match*self.callback.borrow(){
Some(ref callback)=>(callback)(),
无=>(),
}
}
}
结构按钮{
背景:Arc,
可点击:圆弧,
}
impl按钮{
新发布(上下文:Arc)->Arc{
让clickable=clickable::new(context.clone());
让按钮=弧::新建(按钮{
上下文:上下文,
clickable:clickable.clone(),
});
让tmp|u callback=Box::new(||){
按钮。做一些事情();
});
可点击。设置_回调(一些(tmp_回调));
按钮
}
酒吧fn do_stuff(&self){
//做疯狂的事
设muti=0;
对于0..100中的j{
i=j*i;
}
}
发布fn单击(&self){
self.clickable.click();
}
}
按钮的插入{
fn下降(&mut自我){
Clickable::remove(self.Clickable.clone());
}
}
fn main(){
让context=context::new();
let button=button::new(context.clone());
按钮。单击();
}
我只是不知道如何在闭包中传递引用


另一件丑陋的事情是我的
处理程序
和我的
上下文
相互需要。有没有更好的方法来创建此依赖关系?

脱离初始代码

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
}
注意底部的
帮助
块,您需要使用
移动
闭包,因为当
新建
函数结束时,堆栈上的
按钮
变量将超出范围。避免这种情况的唯一方法是将其所有权转移到回调本身。所以你会改变

let tmp_callback = Box::new(|| {

现在,您将得到第二个错误:

错误[E0382]:使用移动值:`按钮`
-->src/main.rs:107:9
|
102 |让tmp|u callback=Box::new(移动| |{
|------此处的值已移动(进入闭包)
...
107 |按钮
|^^^^^^移动后此处使用的值
|
=注意:之所以发生移动,是因为'button'的类型为'std::sync::Arc',而该类型不实现'Copy'特性
这里的错误可能更清楚一些。您试图将
按钮
值的所有权移动到回调闭包中,但在返回
函数时,您也在函数体中使用它,并且您不能有两种不同的东西试图拥有该值

解决这个问题的方法很有希望是你猜到的。你必须制作一份你可以拥有的副本。然后你会想要改变

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

现在,您已经创建了一个新的
按钮
对象,并为对象本身返回了一个
,同时还将第二个
的所有权授予回调本身

更新 根据您的评论,这里确实存在循环依赖的问题,因为您的
Clickable
对象拥有对
Button
的引用的所有权,而
Button
拥有对
Clickable
的引用的所有权。解决此问题的最简单方法是从

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

因此,
可点击的
将只对
按钮
进行弱引用,如果不再引用
按钮
,则回调将是不可操作的


你可能还想考虑制作<代码>点击代码>代码>弱引用,而不是强引用,这样你就可以在删除引用的项目时从中删除项目。

YEA,这也是我想出的一个解决方案。是否会被删除,用生命?无论如何感谢你的努力!更新答案。非常感谢。此外,我的回调现在是怪物,因为我有多达4弧的地方,我必须做同样的按钮。你也可以考虑制作<代码>可点击< /COD>一个特性,然后实现该特性为<代码>按钮<代码>那你就注册吧。
let button_clone = button.clone();
let tmp_callback = Box::new(move || {
    button_clone.do_stuff();
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();
    }
});