You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中如何用闭包为SDL2游戏更新回调传递数据?

Rust SDL2游戏开发:如何传递数据给update回调

问题背景

我是Rust和底层编程新手,正在用SDL2开发简单游戏,尝试给update回调传递数据时遇到类型不匹配错误。项目代码如下:

main.rs

fn main() -> Result<(), String> {
  let mut window = CWindow::new("Playground", 1080, 720);

  let rects: Vec<Rect> = ... // 我想传入`update`的数据

  window.set_update(|| update(rects)); // 尝试包装update以传递`rects`,问题出在这里
  window.set_draw(draw);
  window.init()?;
}

fn draw(canvas: &mut Canvas<Window>) -> () {
  // 绘制逻辑
}

fn update(rects: Vec<Rect>) -> () {
  // 更新逻辑
}

cwindow.rs

pub struct CWindow {
    title: String,
    width: u32,
    height: u32,
    callbacks: CWindowCallbacks,
    window: Option<Window>,
}

pub struct CWindowCallbacks {
    update: Option<fn() -> ()>,
    draw: Option<fn(&mut Canvas<Window>) -> ()>,
}

impl CWindow {
    pub fn new(title: &str, width: u32, height: u32) -> CWindow {
        CWindow {
            title: String::from(title),
            width: width,
            height: height,
            callbacks: CWindowCallbacks {
                draw: None,
                update: None,
            },
            window: None,
        }
    }

    pub fn init(&self) -> Result<(), String> {
        if self.callbacks.draw.is_none() {
            panic!("Need draw callback")
        }
        if self.callbacks.update.is_none() {
            panic!("Need update callback")
        }
        //...
        // SDL初始化代码
        //...


        'running: loop {
             
            //...
            // 事件处理
            //...

            self.callbacks.update.unwrap()();
            self.callbacks.draw.unwrap()(&mut canvas);
        }
        Ok(())
    }

    pub fn set_draw(&mut self, draw: fn(&mut Canvas<Window>) -> ()) {
        self.callbacks.draw = Some(draw);
    }

    pub fn set_update(&mut self, update: fn() -> ()) {
        self.callbacks.update = Some(update);
    }
}

错误信息

mismatched types
expected fn pointer `fn()`
      found closure `{closure@src\main.rs:15:23: 15:25}`

解决方案

问题根源

fn()是无状态函数指针,无法携带捕获的变量;而你写的|| update(rects)是捕获了rects的闭包,属于有状态的闭包,二者类型不兼容。要解决这个问题,需要修改CWindow的回调存储方式,支持带环境的闭包。

步骤1:修改CWindow的回调类型

将CWindowCallbacks中的update字段改为Option<Box<dyn FnMut()>>,同时调整set_update方法以接受闭包:

修改后的cwindow.rs:

pub struct CWindow {
    title: String,
    width: u32,
    height: u32,
    callbacks: CWindowCallbacks,
    window: Option<Window>,
}

pub struct CWindowCallbacks {
    // 改用Box<dyn FnMut()>存储带环境的闭包
    update: Option<Box<dyn FnMut()>>,
    draw: Option<fn(&mut Canvas<Window>) -> ()>,
}

impl CWindow {
    pub fn new(title: &str, width: u32, height: u32) -> CWindow {
        CWindow {
            title: String::from(title),
            width,
            height,
            callbacks: CWindowCallbacks {
                draw: None,
                update: None,
            },
            window: None,
        }
    }

    // 注意init方法需要改为&mut self,因为要调用可变闭包
    pub fn init(&mut self) -> Result<(), String> {
        if self.callbacks.draw.is_none() {
            panic!("Need draw callback")
        }
        if self.callbacks.update.is_none() {
            panic!("Need update callback")
        }
        //... SDL初始化代码 ...
        let mut canvas = /* 你的Canvas初始化逻辑 */;

        'running: loop {
            //... 事件处理 ...

            // 调用可变闭包,需要as_mut()获取可变引用
            self.callbacks.update.as_mut().unwrap()();
            self.callbacks.draw.unwrap()(&mut canvas);
        }
        Ok(())
    }

    pub fn set_draw(&mut self, draw: fn(&mut Canvas<Window>) -> ()) {
        self.callbacks.draw = Some(draw);
    }

    // 泛型方法,接受任意符合FnMut()的闭包,装箱后存储
    pub fn set_update<F>(&mut self, update: F)
    where
        F: FnMut() + 'static,
    {
        self.callbacks.update = Some(Box::new(update));
    }
}

步骤2:根据需求传递数据

根据你对rects的使用场景,选择不同的闭包方式:

场景1:传递所有权(一次性使用)

如果update只需要获取rects的所有权,使用move闭包转移所有权:

fn main() -> Result<(), String> {
    let mut window = CWindow::new("Playground", 1080, 720);

    let rects: Vec<Rect> = /* 初始化rects */;

    // move闭包将rects所有权转移到闭包内部
    window.set_update(move || update(rects));
    window.set_draw(draw);
    window.init()?;
}

fn update(rects: Vec<Rect>) -> () {
    // 使用rects,注意所有权已转移,后续无法在main中访问
}

场景2:多次修改rects

如果需要在每次update调用中修改rects,让update接受可变引用,并捕获&mut rects:

fn main() -> Result<(), String> {
    let mut window = CWindow::new("Playground", 1080, 720);

    let mut rects: Vec<Rect> = /* 初始化rects */;

    // move闭包捕获可变引用,闭包内部可以修改rects
    window.set_update(move || update(&mut rects));
    window.set_draw(draw);
    window.init()?;
}

fn update(rects: &mut Vec<Rect>) -> () {
    // 修改rects,比如添加元素、更新位置等
}

内容的提问来源于stack exchange,提问作者Blink Dagger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 02:21:01