调试 Rust + WebAssembly 版康威生命游戏

1. 启用 Panic 日志

1.1 让 Panic 信息显示在浏览器控制台

如果 Rust 代码发生 panic!(),默认情况下不会在浏览器开发者工具中显示详细的错误信息。这使得排查问题变得困难。

我们可以使用 console_error_panic_hook 这个 Rust crate ,将 Panic 信息打印到 浏览器控制台

1.2 在 wasm-game-of-life/src/lib.rs 中修改 new 方法

rust 复制代码
pub fn new() -> Universe {
    utils::set_panic_hook();  // 让 Panic 信息出现在控制台

    // 其他初始化代码...
}

1.3 安装 console_error_panic_hook

如果 wasm-game-of-life/src/utils.rs 还没有 set_panic_hook() 方法,我们需要手动引入 console_error_panic_hook

rust 复制代码
pub fn set_panic_hook() {
    console_error_panic_hook::set_once();
}

这样,当 Rust 代码 崩溃(panic) 时,就能在 浏览器控制台 看到详细的错误信息,包括 Rust 堆栈回溯(stack trace)

2. 添加日志调试

2.1 使用 web_sys::console.log 记录日志

WebAssembly 代码无法直接使用 println!() ,但可以借助 web_sys 将日志输出到 浏览器控制台

首先,在 Cargo.toml 文件中启用 web-sys 依赖

toml 复制代码
[dependencies.web-sys]
version = "0.3"
features = ["console"]

然后,在 wasm-game-of-life/src/lib.rs 定义一个 log!(),用于在 Rust 代码中方便地输出调试信息:

rust 复制代码
extern crate web_sys;

// 定义 `log!()` 宏,类似 `println!()`,但输出到 `console.log`
macro_rules! log {
    ( $( $t:tt )* ) => {
        web_sys::console::log_1(&format!( $( $t )* ).into());
    }
}

2.2 在 tick() 方法中记录细胞状态

我们可以在 tick() 方法中 记录每个细胞的状态变化

rust 复制代码
impl Universe {
    pub fn tick(&mut self) {
        let mut next = self.cells.clone();

        for row in 0..self.height {
            for col in 0..self.width {
                let idx = self.get_index(row, col);
                let cell = self.cells[idx];
                let live_neighbors = self.live_neighbor_count(row, col);

                // 记录当前细胞的状态和活邻居数量
                log!(
                    "cell[{}, {}] is initially {:?} and has {} live neighbors",
                    row, col, cell, live_neighbors
                );

                let next_cell = match (cell, live_neighbors) {
                    (Cell::Alive, x) if x < 2 => Cell::Dead,  // 过少 -> 死亡
                    (Cell::Alive, 2) | (Cell::Alive, 3) => Cell::Alive,  // 繁衍 -> 存活
                    (Cell::Alive, x) if x > 3 => Cell::Dead,  // 过度拥挤 -> 死亡
                    (Cell::Dead, 3) => Cell::Alive,  // 复活
                    (otherwise, _) => otherwise,
                };

                // 记录细胞变化
                if next_cell != cell {
                    log!(
                        "    cell[{}, {}] transitioned from {:?} to {:?}",
                        row, col, cell, next_cell
                    );
                }

                next[idx] = next_cell;
            }
        }

        self.cells = next;
    }
}

3. 在 JavaScript 中使用 debugger 进行断点调试

3.1 在 renderLoop() 中设置断点

我们可以在 renderLoop()手动插入 debugger; 语句 ,让浏览器在每次 tick() 之前 暂停代码执行

js 复制代码
const renderLoop = () => {
  debugger;  // 断点调试,暂停执行

  universe.tick();
  drawGrid();
  drawCells();

  requestAnimationFrame(renderLoop);
};

3.2 调试步骤

  1. 打开开发者工具(F12 或右键 → Inspect)
  2. 进入 "Sources" 选项卡
  3. 执行 npm run start 运行游戏
  4. 游戏在 debugger; 处暂停 ,你可以:
    • 逐步执行代码(Step Over)
    • 观察变量的值
    • 查看 WebAssembly 内存状态

4. 练习

练习 1:记录状态变化

任务 :在 tick() 方法中,仅记录发生状态变化的细胞 (从 AliveDeadDeadAlive)。
实现

rust 复制代码
if next_cell != cell {
    log!(
        "cell[{}, {}] transitioned from {:?} to {:?}",
        row, col, cell, next_cell
    );
}

练习 2:手动触发 Panic**

任务 :在 new() 方法中 故意触发 panic ,观察不同情况下的 浏览器堆栈追踪
实现

rust 复制代码
pub fn new() -> Universe {
    utils::set_panic_hook();
    
    panic!("Intentional panic for debugging!");  // 触发崩溃
}

步骤

  1. 运行 npm run start
  2. 在控制台查看 panic!() 抛出的错误
  3. 尝试关闭 console_error_panic_hook 并重新测试
    • Cargo.toml 移除 console_error_panic_hook
    • 运行 wasm-pack build
    • 观察 错误信息变得难以理解,因为 Rust Panic 追踪信息消失了。

5. 总结

调试技巧 作用
console_error_panic_hook 让 Rust Panic 信息出现在浏览器控制台
log!() 在 Rust 代码中使用 console.log() 输出日志
记录状态变化 仅在细胞状态改变时记录日志,减少无用信息
debugger; 断点 让浏览器暂停在 tick() 之前,手动调试
手动 panic!() 观察 Panic 追踪信息,学习如何优化错误信息

6.下一步

  • 优化 WebAssembly 性能
    • 使用 位运算优化细胞存储(每个细胞只占 1 bit,而不是 1 byte)
    • 使用 Web Workers 进行并行计算
  • 增加交互
    • 让用户 手动绘制初始图案
    • 控制 tick() 速度

通过 Rust + WebAssembly ,我们可以在 浏览器中高效运行生命游戏 ,并通过 强大的调试工具 确保代码的正确性!🚀

相关推荐
无名之逆2 小时前
Hyperlane:Rust 语言打造的 Web 后端框架新标杆
开发语言·前端·网络·网络协议·rust·github·ssl
勇敢牛牛_4 小时前
【Rust交叉编译】在x86_64架构下交叉编译aarch64-linux-musl版的rust-opencv
linux·opencv·rust·aarch64
cyr___4 小时前
Unity教程(二十二)技能系统 分身技能
学习·游戏·unity·游戏引擎
我命由我123457 小时前
游戏引擎 Unity - Unity 顶部菜单栏(文件、编辑、资源、游戏对象、组件、服务、窗口、帮助)
学习·游戏·unity·ue5·游戏引擎·游戏程序·游戏策划
Cindy辛蒂10 小时前
C语言:能够规定次数,处理非法字符的猜数游戏(三重循环)
c语言·算法·游戏
helloweilei11 小时前
Rust学习笔记 之 char类型
rust
红尘散仙11 小时前
三、WebGPU 基础入门——绘制三角型
rust·typescript·gpu
米芝鱼12 小时前
Unity URPShader:实现和PS一样的色相/饱和度调整参数效果
游戏·unity·游戏引擎·shader·urp·ps·hlsl
鹿屿二向箔13 小时前
一个简单的井字棋(Tic-Tac-Toe)游戏的C语言实现
c语言·开发语言·游戏