- 开发工具
【免费下载链接】wasm-bindgen
Facilitating high-level interactions between Wasm modules and JavaScript
导读
inspectable是 wasm-bindgen 提供在 Rust 导出结构体(struct)上的属性,用于自动生成toJSON与toString两个 JavaScript 方法,使导出类不再只暴露一个ptr属性,而是可以把所有可读字段完整序列化出来,便于日志输出、调试与数据交换。本文以 官方参考文档 为骨架,结合仓库中的宏解析(parser.rs)、JS 代码生成(js/mod.rs)与端到端测试(classes.rs、classes.js)深入讲解其行为、覆盖范围、自定义覆盖方式及 Node.js 下的特殊支持,读完你可以直接在项目里为导出结构体启用并微调该特性。
默认行为:为什么导出类“看不见”字段
默认情况下,从 Rust 导出的结构体会被编译成 JavaScript 类,类实例上只有一个ptr属性(实际持有指向 Wasm 线性内存中 Rust 对象指针的__wbg_ptr)。其他所有字段虽然会生成对应的 getter 访问器,但 getter 不会被JSON.stringify、toJSON或console.log枚举出来——也就是说,直接序列化实例时看不到任何业务字段。
该行为可以从代码生成端得到印证:js/mod.rs 的类导出逻辑 中明确指出,只有当class.is_inspectable为真时才生成toJSON与toString,否则类只有ptr相关成员。测试 classes.js 也验证了这一点:
// 未加 inspectable 的类没有生成 toJSON/toString assert.strictEqual(not_inspectable.toJSON, undefined); assert.strictEqual(not_inspectable.toString(), '[object Object]'); // Node 下 console.log 只显示 __wbg_ptr // 形如:NotInspectable { __wbg_ptr: 123456 }使用inspectable:一行属性获得完整序列化
在导出结构体的#[wasm_bindgen(...)]属性列表中加入inspectable即可。官方文档给出的完整示例:
#[wasm_bindgen(inspectable)] pub struct Baz { pub field: i32, private: i32, // 私有字段不会被导出,除非单独提供 getter } #[wasm_bindgen] impl Baz { #[wasm_bindgen(constructor)] pub fn new(field: i32) -> Baz { Baz { field, private: 13 } } }编译后在 JavaScript 中可以得到如下行为:
const obj = new Baz(3); assert.deepStrictEqual(obj.toJSON(), { field: 3 }); // 只包含可读字段 obj.field = 4; assert.strictEqual(obj.toString(), '{"field":4}'); // toString 内部调用 toJSON属性解析链路
inspectable在宏解析阶段被登记为合法属性(parser.rs:(inspectable, false, Inspectable(Span))),随后通过attrs.inspectable().is_some()读取(parser.rs),并随结构体元数据一路传递到 CLI 侧,最终存入 nonstandard.rs 中的AuxStruct::is_inspectable,再由 js/mod.rs 生成 JS 代码。
生成代码的形态
对普通命名结构体,CLI 生成的toJSON形如:
toJSON() { return { field: this.field, // 所有可读字段(public 字段 + 公开 getter)都会被收集 }; } toString() { return JSON.stringify(this); }代码生成逻辑见 js/mod.rs,生成规则要点:
- **可读字段集合(
readable_properties)**由两部分构成:pub字段,以及通过 getter 暴露的属性;在收集公开 getter 时会跳过符号键(Symbol)字段,因为toJSON只能收集字符串键属性(js/mod.rs)。 - 字段名如果是合法标识符,则用
field: this.field的形式;若不是(如元组结构体的0、1),则使用带引号键"0": this["0"],避免生成非法 JavaScript(js/mod.rs)。 - 同时会向
.d.ts声明文件写入对应类型声明:toJSON(): Object;与toString(): string;,并附带注释说明语义(js/mod.rs)。完整声明示例可查看参考测试的 inspectable.d.ts。
元组结构体:编号字段的序列化
inspectable同样适用于元组结构体。参考测试 inspectable.rs 中的Pair(pub u32, pub i32)在--target=nodejs下生成的 inspectable.js 为:
toJSON() { return { "0": this["0"], "1": this["1"], }; } toString() { return JSON.stringify(this); }对应的端到端测试 classes.js 验证其行为:
const tuple = wasm.InspectableTuple.new(1, -2); // 元组字段以属性 "0"、"1" 暴露 assert.deepStrictEqual(tuple.toJSON(), { 0: 1, 1: -2 }); assert.strictEqual(tuple.toString(), '{"0":1,"1":-2}');这一行为经过一次修复:早期版本为元组结构体生成this.0这种非法的 JavaScript 访问形式,现已改为this["0"](见 CHANGELOG.md),本仓库中的生成代码正是修正后的实现。
覆盖生成的toJSON/toString
如果你希望自定义序列化输出,可以用js_name属性覆盖。注意:生成的toString内部调用toJSON,因此覆盖toJSON会连带影响toString的输出。官方文档示例:
#[wasm_bindgen] impl Baz { #[wasm_bindgen(js_name = toJSON)] pub fn to_json(&self) -> i32 { self.field } #[wasm_bindgen(js_name = toString)] pub fn to_string(&self) -> String { format!("Baz: {}", self.field) } }仓库测试 classes.rs 的OverriddenInspectable与对应 JS 断言 classes.js 完整覆盖了该场景:
assert.deepStrictEqual(overridden_inspectable.toJSON(), 'JSON was overwritten'); assert.strictEqual(overridden_inspectable.toString(), 'string was overwritten');浏览器环境中的console.log限制
需要注意:即使加了inspectable,浏览器里console.log的输出依然不会变,仍然只显示ptr字段。这是因为浏览器对console.log的处理不通过toJSON/toString。官方文档建议:在这种场景下调用toJSON()或JSON.stringify(obj)来辅助日志与调试。Node.js 不受此限制,详见下一节。
Node.js 目标下的增强:[util.inspect.custom]
当以nodejs目标编译时,生成器还会额外提供一个[util.inspect.custom]实现,内部调用toJSON。由于 Node.js 的console.log及其同类函数(如util.inspect、console.dir)会优先使用该自定义检查器,因此可以像原生 JS 对象一样打印出全部可读字段。
实现位于 js/mod.rs:生成器从util模块导入inspect,然后生成:
[inspect.custom]() { return Object.assign(Object.create({constructor: this.constructor}), this.toJSON()); }这里通过Object.create({constructor: this.constructor})保留类名,使输出形如Point { x: 1, y: 2 },与普通 JavaScript 类的打印风格一致。参考测试生成的 inspectable.js 与 classes.js 的验证 都体现了这一点:
// Inspectable 类在 Node.js 下 console.log 会显示所有可读字段 assert(console_log_to_string(inspectable).endsWith(`{ a: ${inspectable.a} }`));注意该文件头部的const { inspect } = require('util');正是由 js/mod.rs 的模块导入逻辑 生成的,对应输出可见 inspectable.js。
调试建议与综合示例
综合以上内容,一个实用模式是:
use wasm_bindgen::prelude::*; #[wasm_bindgen(inspectable)] pub struct Point { pub x: u32, pub y: u32, } #[wasm_bindgen] impl Point { #[wasm_bindgen(constructor)] pub fn new(x: u32, y: u32) -> Point { Point { x, y } } }在 Node.js 中:
const p = new Point(3, 4); console.log(p); // Point { x: 3, y: 4 } —— 借助 util.inspect.custom console.log(JSON.stringify(p)); // {"x":3,"y":4} —— 借助 toJSON p.x = 5; console.log(p.toString()); // {"x":5,"y":4} —— toString 内部调用 toJSON而在浏览器控制台中console.log(p)仍只显示ptr属性,此时应显式调用p.toJSON()或JSON.stringify(p)查看字段内容。
小结
| 能力 | 默认结构体 | inspectable结构体 |
|---|---|---|
| 实例字段 | 仅ptr(__wbg_ptr) | 全部可读字段(pub字段 + 公开 getter) |
toJSON() | 无 | 自动生成,返回可读字段对象 |
toString() | [object Object] | 自动生成,JSON.stringify(this)结果 |
浏览器console.log | 仅显示ptr | 仍仅显示ptr(建议用toJSON()/JSON.stringify) |
Node.jsconsole.log | 仅显示__wbg_ptr | 显示所有可读字段(经[util.inspect.custom]) |
| 自定义输出 | — | 用js_name = toJSON/js_name = toString覆盖 |
inspectable属性在 wasm-bindgen 0.12 时代引入(见 CHANGELOG.md),至今仍是导出结构体日志与序列化方案的首选工具。想要深入了解完整属性列表,可查阅 on-rust-exports 目录索引,或直接阅读 官方参考文档 获取最权威的说明。
- 开发工具
【免费下载链接】wasm-bindgen
Facilitating high-level interactions between Wasm modules and JavaScript
相关推荐
TypeGraphQL与Rust集成:使用wasm-bindgen实现高性能组件
TypeGraphQL与Rust集成:使用wasm bindgen实现高性能组件 技术背景与集成价值 TypeGraphQL作为TypeScript优先的Gra
后端GraphQLAPI设计ik_llama.cpp 函数调用(Function Calling)支持全解析:Kimi-K2 原生 Token 格式、Qwen3 XML 与 DeepSeek R1 多格式统一解析
ik_llama.cpp 函数调用(Function Calling)支持全解析:Kimi K2 原生 Token 格式、Qwen3 XML 与 DeepSee
开发工具Nitro Runtime Config 完全指南:定义配置、环境变量覆盖与运行时读取
Nitro Runtime Config 完全指南:定义配置、环境变量覆盖与运行时读取 Nitro(Next Generation Server Toolkit
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考