rust“借用的数据在方法之外逃逸”并带有闭包

问题描述 投票:0回答:2

这里的系统是:

  • 事件:针对不同类型事件“扩展”的特征
  • window:负责窗口创建并将其事件进一步传播到队列的控制器
  • application:整个应用程序的控制器,它创建一个窗口并进行其他操作(目前不重要)。

窗口和应用程序对象必须与程序一样存在,因为它们本身就是程序。

我需要能够创建一个回调函数(闭包或方法)并使用“set_event_callback”方法将其传递给窗口结构,以便在事件发生时调用处理程序。

但是,我面临着生命周期的问题,因为下面的代码编译时不会出现错误:

error[E0521]: borrowed data escapes outside of method
  --> src/main.rs:34:9
   |
31 |       pub fn run(&mut self) {
   |                  ---------
   |                  |
   |                  `self` is a reference that is only valid in the method body
   |                  let's call the lifetime of this reference `'1`
...
34 | /         window.set_event_callback(|event| {
35 | |             self.data += 1;
36 | |             false
37 | |         });
   | |          ^
   | |          |
   | |__________`self` escapes the method body here
   |            argument requires that `'1` must outlive `'static`
trait Event {}

struct Window {
    callback: Box<dyn FnMut(&mut dyn Event) -> bool>
}

impl Window {
    pub fn new() -> Self {
        Self {
            callback: Box::new(|_| false)
        }
    }
    
    pub fn set_event_callback<C>(&mut self, callback: C) 
        where C: FnMut(&mut dyn Event) -> bool + 'static {
        self.callback = Box::new(callback);
    }
}

struct Application {
    data: i32
}

impl Application {
    pub fn new() -> Self {
        Self {
            data: 0
        }
    }
    
    pub fn run(&mut self) {
        let mut window = Window::new();
        
        window.set_event_callback(|event| {
            self.data += 1;
            false
        });
    }
}

fn main() {
    Application::new().run();
}

我怀疑这可能发生,因为编译器不知道

window
application
对象的生命周期。然而,我在一周内找不到解决方案。

注意:

'static
方法
&self
中将
run
添加到
(pub fn run(&'static self))
会导致另一个错误:

error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:42:5
   |
42 |     Application::new().run();
   |     ^^^^^^^^^^^^^^^^^^------
   |     |
   |     creates a temporary value which is freed while still in use
   |     argument requires that borrow lasts for `'static`
43 | }
   | - temporary value is freed at the end of this statement
rust closures lifetime-scoping
2个回答
1
投票

我有第二个解决方案给你。这个抽象了一生。
小小的开销是:

  • 1 堆分配
  • 克隆数据句柄的操作速度较慢
  • 数据操作速度较慢
use std::sync::{
    atomic::{AtomicI32, Ordering},
    Arc,
};

trait Event {}

struct Window {
    callback: Box<dyn FnMut(&mut dyn Event) -> bool>,
}

impl Window {
    pub fn new() -> Window {
        Self {
            callback: Box::new(|_| false),
        }
    }

    pub fn set_event_callback<C>(&mut self, callback: C)
    where
        C: FnMut(&mut dyn Event) -> bool + 'static,
    {
        self.callback = Box::new(callback);
    }
}

struct Application {
    data: Arc<AtomicI32>,
}

impl Application {
    pub fn new() -> Self {
        Self {
            data: Arc::new(AtomicI32::new(0)),
        }
    }

    pub fn run(&self) {
        let mut window = Window::new();

        let data_handle = Arc::clone(&self.data);

        window.set_event_callback(move |_event| {
            data_handle.fetch_add(1, Ordering::SeqCst);
            false
        });
    }
}

fn main() {
    let app = Application::new();
    app.run();
}

0
投票

我已经通过添加显式生命周期注释修复了您的代码
我向编译器保证

callback
的生存时间不会超过它使用的
application
的唯一引用。

    trait Event {}
    
    struct Window<'f> {
        callback: Box<dyn FnMut(&mut dyn Event) -> bool + 'f>,
    }
    
    impl<'f> Window<'f> {
        pub fn new() -> Window<'f> {
            Self {
                callback: Box::new(|_| false),
            }
        }
    
        pub fn set_event_callback<'w, C>(&'w mut self, callback: C)
        where
            C: FnMut(&mut dyn Event) -> bool + 'f,
        {
            self.callback = Box::new(callback);
        }
    }
    
    struct Application {
        data: i32,
    }
    
    impl Application {
        pub fn new() -> Self {
            Self { data: 0 }
        }
    
        pub fn run<'s>(&'s mut self) {
            let mut window = Window::<'s>::new();
    
            window.set_event_callback(|event| {
                self.data += 1;
                false
            });
        }
    }
    
    fn main() {
        let mut app = Application::new();
        app.run();
    }

但请记住通常要避免 Rust 生命周期。它只是C++指针恶魔的更温和的化身,处理起来更安全。
然而它们“通常”很难写。当它们是解决问题的最佳工具时,很少有用例。

© www.soinside.com 2019 - 2024. All rights reserved.