< 返回版块

T 发表于 2022-08-23 19:07

Tags:closure

我在尝试winit(和wgpu)库的时候,在一个闭包里对数据可空引用进行重新赋值操作,出现了如题目所示的问题。我把它抽离出一个简单的模型如下:

// 这个Color 代表了被引用的数据
#[derive(Debug)]
struct Color {
    x: u32,
    y: u64,
}

// 这个State代表了持有数据的引用的一方
#[derive(Debug)]
struct State<'a> {
    v: Option<&'a Color>,
}

// 这个App模拟了winit库里的EventLoop
// run方法的签名是直接搬过来的,即我不能改动这个函数签名
struct App;
impl App {
    fn run<F>(self, mut event_handler: F)
    where
        F: 'static + FnMut(u32),
    {
        // 简化对闭包处理的模拟,直接调用
        event_handler(10);
    }
}

// 这个app_run模拟包含了使用人家run接口的方法
fn app_run(mut state: State) {
    let app = App;
    let color = Color { x: 1, y: 1 };
    app.run(move |_| {
        // 这里对使用情况进行模拟
        if true {
            state.v = Some(&color);  // 这一句引发问题。
        }else{
            state.v = None;
        }
    })
}

fn main() {
    let sate = State{ v: None };
    app_run(sate);
}

这里是playground

原问题代码如下

// State里有一个HashMap类型的render_pipeline,对它使用get得到数据的引用
// Config里current_pipeline是对数据的可空引用。初始化为None
fn app_run<T>(window: Window, event_loop: EventLoop<T>, mut state: State, mut config: Config) {
    event_loop.run(move |ev, _tgt, control| match ev {
        Event::WindowEvent {
            window_id,
            ref event,
        } if window.id() == window_id => match event {
            WindowEvent::KeyboardInput {
                input:
                    KeyboardInput {
                        state: ElementState::Pressed,
                        virtual_keycode: Some(VirtualKeyCode::Space),
                        ..
                    },
                ..
            } => match config.current_pipeline {
                Some(v) => {
                    if let Some(nv) = state.render_pipeline.get("base pipeline") {
                        if std::ptr::eq(v, nv) {
                            // 如果没有这种赋值是可以运行的。
                            config.current_pipeline = state.render_pipeline.get("color pipeline");
                        };
                    } else {
                        config.current_pipeline = state.render_pipeline.get("base pipeline");
                    }
                }
                None => config.current_pipeline = state.render_pipeline.get("base pipeline"),
            },
            _ => {}
        },
        _ => {}
    });
}

评论区

写评论
dlhxzb 2022-08-24 14:42

'static意味着State内不能有引用,即不依赖其它变量生命周期 FnMut意味着State里color不引用的话就要实现Clone或Copy,以便捕获后多次调用

苦瓜小仔 2022-08-23 22:38

你的代码有两处错误:

  1. 因为一个被要求满足 'static 的闭包,捕获了非 'static 的变量(变量 state 的生命周期参数不为 'static),所以导致你看到的错误。
  2. 把生命周期标注的省略规则补全:fn app_run(mut state: State<'1>),state 包含外部的引用 &'1,所以你不可能让 state 得到函数内的 color 变量的引用。

要解决你遇到的生命周期的问题,必须重构代码(从类型到逻辑都要考虑到),尤其不能让 F: 'static + FnMut(u32) 闭包捕获非 'static 的引用或者非 'static 生命周期参数的类型。

1 共 2 条评论, 1 页