每次把 Rust 跑进浏览器都要先查一遍 target-features?——今天这件事被 LLVM 19 从根上修了
如果你试过把 Rust 编译成 WebAssembly,大概遇到过这种情况:代码本地跑得好好的,编译成 .wasm 之后在浏览器里一跑就报 unreachable 或者某个 import 找不到。查半天发现不是逻辑问题,是某个 Wasm 特性没有在编译目标里开启——reference-types、multi-value……这些字段名散落在各种博客的边边角角,没有一份统一文档告诉你哪些应该开、哪些已经默认了。
这个局面在 Rust 1.82 这里彻底变了。
Rust 1.82 升级到 LLVM 19,两个 Wasm 目标特性默认开启
Rust 编译器从 LLVM 18 升级到 LLVM 19,直接影响是:WebAssembly 编译目标的默认功能集变了。具体来说,reference-types(引用类型)和 multi-value(多值返回)这两个提案现在默认启用,不再需要手动加 -C target-feature=+reference-types,+multi-value。
这不是小事。reference-types 让 Wasm 模块可以直接操作 externref 和 funcref,是 Rust 闭包和 JS 函数互相传递的基础设施;multi-value 允许一个 Wasm 函数返回多个值,省掉了大量打包解包中间结构的样板代码。这两个加在一起,几乎覆盖了 wasm-bindgen 在编译时最常做的两类 hack。
为什么要等 LLVM 19 才能默认?
LLVM 是 Rust 底层的代码生成后端,Wasm 特性支持需要 LLVM 本身先实现,然后 Rust 工具链才能暴露为稳定的默认选项。LLVM 19 在今年正式合并了这两个提案的默认启用逻辑,Rust 随之在 1.82 里跟进。这意味着从 Rust 1.82 开始,新项目的 wasm32-unknown-unknown 目标开箱即用,不需要再查文档确认要不要加 --features 参数。
对于已经在维护的存量项目影响有限——之前手动开启的特性不会因为默认值变了而冲突。但如果你用的是老版本 Rust(<1.76),或者 CI 镜像里的工具链版本停在半年前,现在可能是升级的好时机。
两个场景感受具体差别
第一个是 JS 调用 Rust 闭包。之前 wasm-bindgen 需要在编译产物里做一层包装,把 JS 传过来的函数引用转成 u32 句柄再转回来——因为没有原生的 funcref 支持。reference-types 默认开启之后,这层转换消失了,直接传直接调,产物更小,调用链更快。
第二个是多值返回函数。Rust 里经常出现一个函数返回 Result<(A, B), Error> 然后调用方立刻解构的场景。在 Wasm 里没有 multi-value 的时候,这个元组会被拆成单独内存地址传参传回,开销不低。现在直接作为两个值从函数返回,LLVM 可以更好地做寄存器分配。
现在该不该改什么?
如果你的项目已经在用 Rust 1.80+ 并且能正常跑在浏览器里,不需要立刻行动。但有两个值得注意的地方:
一是 CI 里的构建命令。如果你有 -C target-feature=+reference-types,+multi-value 这样的显式参数,可以试着去掉,然后跑一遍测试看看是否仍然正常——大概率会,发现不能跑的情况再回退也不迟。
二是依赖版本。wasm-pack、wasm-bindgen 这类工具今年都有更新,LLVM 19 的默认特性组合在它们的新版本里经过了更多实战验证。如果你还跑着 2025 年的旧版工具链,顺手升级一下能减少意外。
下一步
Rust 在 Wasm 生态里的角色这几年一直在变——从最早「能编译过去就行」的实验场,到今天和 WebGPU、Component Model 深度绑定的正经生产选项。LLVM 19 这次默认开启只是一个小变化,但它背后是整个工具链链路的成熟。如果你还没把任何 Rust 模块跑进过浏览器,现在是好时候——工具链的坑比两年前少太多了。
评论区
登录后可评论。