为什么我会收到一个“借用的数据逃逸出方法之外”错误?

huangapple go评论118阅读模式
英文:

Why do I get a "borrowed data escapes outside of method" error?

问题

我有一个名为 renderer 的变量,我必须在事件循环中进行编辑。问题是,在我的 App 结构体的 run 函数中,事件循环总是出现相同的 borrowed data escapes outside of method 错误。我如何修复这个问题,同时仍然能够在运行时调整 renderer 的值?我看到的所有解决方案都导致某种形式的复制被传递,这不是我所需要的。感谢帮助!

完整错误:

error[E0521]: borrowed data escapes outside of method
  --> src/app.rs:54:9
   |
50 |       pub fn run(mut self, renderer: &mut Renderer) -> Result<(), pixels::Error> {
   |                            --------  - let's call the lifetime of this reference `'1`
   |                            |
   |                            `renderer` is a reference that is only valid in the method body
...
54 | /         self.event_loop.run(move |event, _, control_flow| {
55 | |             if let Event::RedrawRequested(_) = event {
56 | |
57 | |                 let current_frame_time = Instant::now();
...  |
84 | |             }
85 | |         });
   | |          ^
   | |          |
   | |__________`renderer` escapes the method body here
   |            argument requires that `'1` must outlive `'static`

有关完整的、较长的代码,请查看我的 GitHub 存储库:https://github.com/KGL8/RayTracingPaper/tree/working_snapshot

App.rs 的简化版本:

pub struct App {
    // ***
}

impl App {
    pub fn new() -> Result<Self, pixels::Error> {
        // ***
    }

    pub fn create_window(event_loop: &EventLoop<()>) -> Window {
        // ***
    }

    pub fn run(mut self, renderer: &mut Renderer) -> Result<(), pixels::Error> {

        let mut previous_frame_time = Instant::now();

        self.event_loop.run(move |event, _, control_flow| {
            if let Event::RedrawRequested(_) = event {

                let current_frame_time = Instant::now();
                let timestep = current_frame_time.duration_since(previous_frame_time).as_secs_f32();
                previous_frame_time = current_frame_time;
                renderer.on_update(timestep, &self.input);

                if let Err(err) = renderer.render(&mut self.pixels) {
                    log_error("renderer.draw_frame", err);
                    *control_flow = ControlFlow::Exit;
                    return;
                }
            }

            // ***

                self.window.request_redraw();
            }
        });
    }
}
英文:

I have a variable renderer that I have to edit over an event loop. The issue is that I keep getting the same borrowed data escapes outside of method error for the event loop in the run function of my App struct. How do I fix this with still being able to adjust the values of renderer over runtime? All the solutions I've seen resulted in some form of copy being passed, which is not what I need. Thanks for the help!

Full error:

error[E0521]: borrowed data escapes outside of method
  --&gt; src/app.rs:54:9
   |
50 |       pub fn run(mut self, renderer: &amp;mut Renderer) -&gt; Result&lt;(), pixels::Error&gt; {
   |                            --------  - let&#39;s call the lifetime of this reference `&#39;1`
   |                            |
   |                            `renderer` is a reference that is only valid in the method body
...
54 | /         self.event_loop.run(move |event, _, control_flow| {
55 | |             if let Event::RedrawRequested(_) = event {
56 | |
57 | |                 let current_frame_time = Instant::now();
...  |
84 | |             }
85 | |         });
   | |          ^
   | |          |
   | |__________`renderer` escapes the method body here
   |            argument requires that `&#39;1` must outlive `&#39;static`

For the full, long code, here is my GitHub repo: https://github.com/KGL8/RayTracingPaper/tree/working_snapshot

Shortened version of my App.rs:

pub struct App {
    // ***
}

impl App {
    pub fn new() -&gt; Result&lt;Self, pixels::Error&gt; {
        // ***
    }

    pub fn create_window(event_loop: &amp;EventLoop&lt;()&gt;) -&gt; Window {
        // ***
    }

    pub fn run(mut self, renderer: &amp;mut Renderer) -&gt; Result&lt;(), pixels::Error&gt; {

        let mut previous_frame_time = Instant::now();

        self.event_loop.run(move |event, _, control_flow| {
            if let Event::RedrawRequested(_) = event {

                let current_frame_time = Instant::now();
                let timestep = current_frame_time.duration_since(previous_frame_time).as_secs_f32();
                previous_frame_time = current_frame_time;
                renderer.on_update(timestep, &amp;self.input);

                if let Err(err) = renderer.render(&amp;mut self.pixels) {
                    log_error(&quot;renderer.draw_frame&quot;, err);
                    *control_flow = ControlFlow::Exit;
                    return;
                }
            }

            // ***

                self.window.request_redraw();
            }
        });
    }
}

答案1

得分: 3

你正在将一个可变引用 renderer 传递给 event_loop.run,而 event_loop.run 是一个具有 'static 生命周期的闭包。

让我们看看 EventLoop::run源代码

    pub fn run<F>(self, event_handler: F) -> !
    where
        F: 'static + FnMut(Event<'_, T>, [ ... ] ),

'static + FnMut 部分表示“此参数是一个具有静态生命周期的闭包”。稍微解释一下:

  • 闭包类似于函数,但它与调用函数所需的所有其他变量一起“捆绑在一起”
  • 'static 是一个特殊的生命周期约束,它告诉 Rust 我们的闭包不会借用任何具有非 'static 生命周期的数据

由于事件循环希望无限调用 event_handler,所以 event_handler 必须在程序运行时继续存在。你正确地使用了一个 move 闭包,这会导致你的闭包中引用的所有变量被捕获并移动到闭包中(这就是上面提到的“捆绑”)。但是,renderer 是有问题的,因为它是一个引用,所以我们无法将其移动到闭包中。

我们有一个必须在程序结束之前存在的闭包,但包含一个在程序结束时就会消失的引用!这就是编译器所说的 borrowed data escapes outside of method 的意思。

为了解决这个问题,我们需要向编译器承诺 renderer 将一直存在直到程序退出。最简单的方法是将其变为 'static
App::run() 中:

pub fn run(mut self, renderer: &'static mut Renderer)

然后,在实例化 Renderer 时,使用 lazy_static

lazy_static! {
    static ref RENDERER: Renderer = Renderer::new();
}
英文:

You are passing renderer, which is a mutable reference, into event_loop.run, which is closure with a &#39;static lifetime.

Let's look at the source for EventLoop::run

    pub fn run&lt;F&gt;(self, event_handler: F) -&gt; !
    where
        F: &#39;static + FnMut(Event&lt;&#39;_, T&gt;, [ ... ] ),

The &#39;static + FnMut part means "this argument is a closure with a static lifetime". To break it down a bit:

  • a closure is a lot like function, but it's "bundled together" with all other variables needed to call the function
  • &#39;static is a special lifetime bound, it tells rust our closure will not borrow any data with non-&#39;static lifetime

Since the event loop wants to call the event_handler indefinitely, event_handler has to continue to exist as long as the program is running.
You correctly used a move closure, which causes all the variables referred to in your closure to be captured and moved into the closure (that's the "bundling" above). renderer is problematic, because it's a reference, so we can't move it into the closure.

We have a closure that has to live until the program ends, yet includes a reference that will die as soon as the program ends! This is what the compiler means by borrowed data escapes outside of method.

To solve this, we need to promise the compiler that renderer will stick around until the program exits. Easiest way is to make it &#39;static,
In App::run()

pub fn run(mut self, renderer: &amp;&#39;static mut Renderer)

then, when you instantiate Renderer, use lazy_static:

lazy_static! {
    static ref RENDERER: Renderer = Renderer::new();
}

huangapple
  • 本文由 发表于 2023年6月29日 06:20:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76577042.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定