1. 启用 Panic 日志
1.1 让 Panic 信息显示在浏览器控制台
如果 Rust 代码发生 panic!()
,默认情况下不会在浏览器开发者工具中显示详细的错误信息。这使得排查问题变得困难。
我们可以使用 console_error_panic_hook
这个 Rust crate,将 Panic 信息打印到 浏览器控制台。
wasmgameoflifesrclibrs__new__7">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
依赖:
[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()
之前 暂停代码执行:
const renderLoop = () => {debugger; // 断点调试,暂停执行universe.tick();drawGrid();drawCells();requestAnimationFrame(renderLoop);
};
3.2 调试步骤
- 打开开发者工具(F12 或右键 → Inspect)
- 进入 “Sources” 选项卡
- 执行
npm run start
运行游戏 - 游戏在
debugger;
处暂停,你可以:- 逐步执行代码(Step Over)
- 观察变量的值
- 查看 WebAssembly 内存状态
4. 练习
练习 1:记录状态变化
任务:在 tick()
方法中,仅记录发生状态变化的细胞(从 Alive
→ Dead
或 Dead
→ Alive
)。
实现:
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!"); // 触发崩溃
}
步骤:
- 运行
npm run start
- 在控制台查看
panic!()
抛出的错误 - 尝试关闭
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,我们可以在 浏览器中高效运行生命游戏,并通过 强大的调试工具 确保代码的正确性!🚀