代码之家  ›  专栏  ›  技术社区  ›  hodasemi

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

  •  6
  • hodasemi  · 技术社区  · 7 年前

    我才刚开始学生锈。为此,我在Rust中重写了我的C++项目,但最大的问题是闭包的生命周期等等。

    here 及以下内容:

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

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

    另一件丑陋的事是我的 Handler 还有我的 Context 彼此需要。有没有更好的方法来创建这种依赖关系?

    1 回复  |  直到 7 年前
        1
  •  4
  •   Shepmaster Lukas Kalbertodt    7 年前

    去掉你的初始代码

    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
    }
    

        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

    let tmp_callback = Box::new(|| {
    

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

        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
    

    这里的错误可能更清楚一些。您试图转移 而且 在身体内部使用

    可以 取得所有权。然后你会想要改变

    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 对于对象本身,同时也给予第二个 回调本身。

    使现代化

    鉴于您的评论,这里确实存在循环依赖性问题,因为您的 Clickable 对象拥有对的引用的所有权 按钮 按钮 可点击

    让button\u 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();
        }
    });
    

    所以 按钮 ,如果 不再引用,则回调将为no-op。

    你可能还想考虑 clickables Weak