从原始 Import/Export 到 wasm-bindgen 与自定义 Section
1. 基础能力:在 wasm 层导入 / 导出函数
1.1 Rust 侧 —— extern & #[no_mangle]
// ① 从 JS 模块 "mod" 导入 foo()
#[link(wasm_import_module = "mod")]
extern "C" {fn foo(); // 只能使用数字类型参数 / 返回值
}// ② 导出 bar() 给 JS 调用
#[no_mangle]
pub extern "C" fn bar() {// ...
}
#[link(wasm_import_module)]:指定 module name,不写默认为"env"。#[no_mangle]:保持符号名bar不被 Rust 重整(mangling),从而在 wasm 导出表中可见。- wasm 目前仅支持 i32 / i64 / f32 / f64 原始数值类型,无指针 / 结构体。
1.2 JS 侧 —— ES6 WebAssembly.instantiate
import wasmUrl from "./pkg/app_bg.wasm?url";const memory = new WebAssembly.Memory({ initial: 1 });
const imports = {mod: { foo: () => console.log("JS foo() 被调用") },env: { memory }, // 默认导入 memory,也可使用 wasm 自带
};const { instance } = await WebAssembly.instantiateStreaming(fetch(wasmUrl),imports,
);// 调用 Rust 导出的 bar()
instance.exports.bar();
- module name + import name 必须与 Rust 侧匹配。
- 实例化后,通过
instance.exports.<fn>调用 Rust 函数。
2. 超越纯数字:共享内存 & wasm-bindgen
2.1 线性内存模型
-
wasm 拥有独立的连续字节数组(
ArrayBuffer),JS 可读写。 -
wasm 代码 无法直接触碰 任何 JS 对象或 DOM;只能看见数字、读写 memory。
-
因此高级数据传递需 两步:
- Copy:JS 把字节写入 wasm memory,再传入指针+长度。
- Handle:创建一个 JS “堆”数组,存放对象引用,在 wasm 里用整数 ID 代表对象。
2.2 wasm-bindgen:胶水自动生成
use wasm_bindgen::prelude::*;// 导出给 JS
#[wasm_bindgen]
pub fn greet(name: &str) {alert(&format!("Hello, {name}!"));
}// 从 JS 导入
#[wasm_bindgen]
extern "C" {#[wasm_bindgen(js_namespace = console)]fn log(s: &str);
}
wasm-bindgen会生成 JS glue code,自动完成字符串复制、对象句柄管理;- JS 侧直接
import init from './pkg/app.js',无需手写内存拷贝逻辑。
3. Custom Section:在 wasm 中嵌入自定义数据
3.1 Rust 写入自定义段
#[link_section = "hello"]
pub static HELLO_SECTION: [u8; 24] = *b"This is a custom section";
link_section将静态数组放入 wasm 文件名为hello的自定义段中。- 内容为任意二进制数据,编译后 只读。
3.2 JS 读取自定义段
const mod = await WebAssembly.compileStreaming(fetch("sections.wasm"));
const buffers = WebAssembly.Module.customSections(mod, "hello");const text = new TextDecoder().decode(buffers[0]);
console.log(text); // "This is a custom section"
customSections(module, name)返回ArrayBuffer[],可能存在多段同名 section。- 常见用途:版本号、元数据、校验和等随模块分发。
4. 典型数据交互套路
| 需求 | 方案 |
|---|---|
| 传递大字符串 / Uint8Array | JS TextEncoder → 写入 memory → Rust slice::from_raw_parts |
| wasm 调 JS DOM | wasm 储存 elementId 整数 → JS imports 提供操作函数 |
| 回调 / 事件 | 在 JS 堆里存闭包,返回句柄;wasm 保存句柄并回调时再由 JS 执行 |
实际项目中,优先使用 wasm-bindgen / js-sys / web-sys 自动生成绑定,极大简化指针与堆句柄管理。
5. 小结
- 裸 import/export 仅适合数值级互调;字符串与复杂对象需自行处理内存。
- wasm-bindgen = 自动绑 glue + 把 Rust
String/Vec<u8>↔ JS 对象互转。 - Custom Section 让你在模块中注入任意元数据,JS 端可在实例化前读取。
- 熟练掌握三板斧:extern/Memory、bindgen、customSections,即可完成从低级数值 API 到高级对象交互的全链路开发。祝你玩转 Rust + WebAssembly!
