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

news/2025/3/19 10:26:06/

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 调试步骤

在这里插入图片描述

  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,我们可以在 浏览器中高效运行生命游戏,并通过 强大的调试工具 确保代码的正确性!🚀


http://www.ppmy.cn/news/1580293.html

相关文章

解决uni-app授权弹框华为审核拒绝

背景&#xff1a; 在使用定位、相机、文件、电话&#xff0c;需要用户同意授权时&#xff0c;华为和vivo需要告知用户使用权限目的。 方案&#xff1a; 在uni授权时&#xff0c;弹框告诉授权目的&#xff0c;效果如下&#xff1a; 代码&#xff1a; const perListener {//…

AI爬虫 :Crawl4AI的安装和详细使用案例(开源 LLM 友好型网络爬虫)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 1. Crawl4AI概述1.1 Crawl4AI 介绍1.2 Crawl4AI 做什么?1.3 Crawl4AI 的核心理念1.4 Crawl4AI v0.5.0 新功能2. Crawl4AI的安装和第一个案例2.1 Crawl4AI 的安装2.2 初始设置2.3 诊断2.4 第一个案例2.5 高级安装(可选…

Linux top 命令详解:从入门到高级用法

Linux top 命令详解&#xff1a;从入门到高级用法 在 Linux 系统中&#xff0c;top 是一个强大的实时监控工具&#xff0c;用于查看系统资源使用情况和进程状态。它可以帮助你快速了解 CPU、内存、负载等信息&#xff0c;是系统管理员和开发者的日常利器。本文将从基本用法开始…

Netty基础—8.Netty实现私有协议栈一

大纲 1.私有协议介绍 2.私有协议的通信模型 3.私有协议栈的消息定义 4.私有协议栈链路的建立 5.私有协议栈链路的关闭 6.私有协议栈的心跳机制 7.私有协议栈的重连机制 8.私有协议栈的重复登录保护 9.私有协议栈核心的ChannelHandler 10.私有协议栈的客户端和服务端 …

The Rust Programming Language 学习 (六)

包和crate和模块 包和crate crate 是一个二进制项或者库。crate root 是一个源文件&#xff0c;Rust 编译器以它为起始点&#xff0c;并构成你的 crate 的根模块,包&#xff08;package&#xff09;是提供一系列功能的一个或者多个 crate。一个包会包含有一个 Cargo.toml 文件…

LeetCode[142] 环形链表 II

哈希表匹配法 set存储遍历过的节点每次遍历查询set中是否有该节点 有&#xff0c;则代表该节点为环的起点无&#xff0c;则插入set中&#xff0c;继续遍历链表 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNo…

【软件工程】01_软件工程的概述

1. 定义 软件是计算机系统中与硬件相互依存的另一部分&#xff0c;它是包括程序&#xff0c;数据及其相关文档的完整集合。 2. 软硬件失效 3. 软件危机 软件危机&#xff08;Software Crisis&#xff09;&#xff1a;指由于落后的软件生产方式无法满足迅速增长的计算机软件需求…

函数模板

一.什么是函数模板呢&#xff1f; 我们拿交换函数来举个例子。 我们要实现这个交换功能&#xff0c;我们可能得写两个函数。 使用函数重载虽然可以实现&#xff0c;但是有一下几个不好的地方&#xff1a; 1. 重载的函数仅仅是类型不同&#xff0c;代码复用率比较低&#xff0…