Rust 和 WebAssembly 的愿景
Rust 和 WebAssembly 可以以多种令人愉悦的方式结合在一起。为了整合我们的努力并产生最大、最积极的影响,Rust 和 WebAssembly 领域工作组专注于一个愿景
将编译为 WebAssembly 的 Rust 代码插入到 JavaScript 代码中,应该是加速最性能敏感的 JavaScript 代码路径的最佳选择。不要丢弃你现有的代码库,因为 Rust 可以很好地与其他语言协作。无论你是 Rust 开发者还是 Web 开发者,你的自然工作流程都不应该改变,因为编译为 wasm 的 Rust 代码可以无缝集成到你的首选工具中。
这篇博文将扩展这些愿望,并描述我们目前在这些愿望方面的进展。在一系列后续文章中,我们将讨论 Rust 和 WebAssembly 生态系统中每个主要组件的下一步计划。
你是否想帮助我们实现这些理想?加入 Rust 和 WebAssembly 领域工作组!
为什么专注于性能敏感的代码?
在最性能敏感的场景中,JavaScript 阻碍了而不是帮助了。它的动态类型系统和非确定性垃圾回收暂停会造成阻碍。看似很小的代码更改会导致性能大幅下降,如果你不小心偏离了 JIT 的最佳路径。
另一方面,Rust 为程序员提供了低级控制和可靠的性能。它没有非确定性垃圾回收暂停。程序员可以控制间接寻址、单态化和内存布局。
使用 Rust,我们不需要成为熟悉每个 JavaScript 实现的 JIT 内部工作机制的性能专家。我们可以在没有魔法的情况下获得速度。
不要重写 - 集成
编译为 WebAssembly 的 Rust 代码没有运行时。这会导致 .wasm
二进制文件的大小很小,与编译为 WebAssembly 的 Rust 代码量成正比。二进制文件的大小非常重要,因为 .wasm
必须通过网络下载。这种比例关系意味着你只为使用的部分付费(以代码大小计)。反过来,这意味着现有的 JavaScript 代码库可以逐步和部分地采用 Rust。
保留已经有效的代码:我们可以只将最性能敏感的 JavaScript 函数移植到 Rust,并立即获得收益。
保留你的工作流程
如果你是一个 JavaScript 黑客,并且想使用一个用 Rust 和 WebAssembly 编写的库,你就不需要改变你的工作流程。我们可以将 .wasm
包发布到 npm,你可以在 package.json
中依赖它们,就像你通常依赖其他 JavaScript 库一样。它们可以作为 ECMAScript 模块、CommonJS 风格的 require
或作为 JavaScript 全局对象的新属性导入。打包工具将像理解 JavaScript 一样理解 Rust 和 WebAssembly。
如果你是一个 Rust 黑客,并且想将你的 crate 编译为 .wasm
并将其分享到 npm,你也不需要改变你的工作流程。事实上,你甚至不需要安装 npm、Node.js 和整个 JavaScript 开发环境。wasm-pack
将编译、优化并为你的 crate 生成 JavaScript 绑定。然后它还会将其发布到 npm!
当前状态
本节提供了我们当前生态系统的快照,目前可用的工具,以及这与上面描述的愿景的比较。
Rust 和 WebAssembly 手册
如果人们无法学习如何自己使用我们构建的一切,那么我们构建的一切都是徒劳的。因此,我们正在编写Rust 和 WebAssembly 手册。
现在,它已经包含了很多很棒的内容
- 启动和运行
- 设计和实现一个非平凡的示例(生命游戏),它集成了 Rust 和 JavaScript
- 调试、时间分析和代码大小分析的技巧
- 如何使用
wasm-pack
发布到 npm
但它缺乏连贯性。它感觉像是附录和随机教程的集合。我们将有一篇后续博文详细介绍它的具体需求,以及如果你有兴趣如何提供帮助。
wasm-bindgen
wasm-bindgen
促进了 Rust 和 JavaScript 之间的通信。 你可以将 JavaScript 对象导入到 Rust 中,并将 Rust 对象导出到 JavaScript 中。它允许你发送字符串和结构体等丰富类型在 wasm 和 JavaScript 之间传递,而不仅仅是 WebAssembly 标准定义的简单整数和浮点数。
以下是使用 wasm-bindgen
在 Rust 和 JavaScript 之间进行“Hello, World!” 的示例。首先,我们将 alert
函数导入到 Rust 中,并将 greet
函数导出到 JavaScript 中
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern {
fn alert(s: &str);
}
#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}
然后,我们在 JavaScript 中将 wasm 作为 ECMAScript 模块导入,并调用 greet
函数
import { greet } from "./hello_world";
greet("World!");
wasm-bindgen
如何工作?简而言之,它是一个过程宏,它接收带有 #[wasm_bindgen]
属性的 Rust 源代码,构建一个抽象语法树 (AST),然后它会生成两个工件
-
导入 JavaScript 对象并导出 Rust 对象的 Rust 绑定。
-
向其他 JavaScript 代码公开 Rust 导出对象的良好接口,并提供 Rust 所需导入的 JavaScript 绑定。
wasm-bindgen
对 JavaScript 绑定的方法允许你只为使用的导入付费。即使你导入了 window.alert
函数,你也不会为 window.document
产生粘合代码。
最大的缺点是,现在你必须自己声明导入。对于 JavaScript 函数和类型以及 Web 平台 API,存在许多人会重复使用的通用导入。手动导入这些内容既枯燥又机械。我们有一个计划来解决这个问题,但你需要等待后续博文才能了解更多信息。
wasm-pack
wasm-pack
旨在成为构建、优化和发布 Rust 生成的 WebAssembly 的一站式商店,你希望在浏览器或 Node.js 中与 JavaScript 交互。 wasm-pack
帮助你构建和发布 Rust 生成的 WebAssembly 到 npm 注册表,以便与你已经使用的任何其他 JavaScript 包一起使用,例如 webpack 等打包工具或 greenkeeper 等服务。
Lin Clark 在 让 WebAssembly 更好地为 Rust 和所有语言服务 中绘制的图
如果你是 Rust 开发者,并且想将编译为 wasm 的 crate 发布到 npm,wasm-pack
将
- 使用
wasm32-unknown-unknown
目标将 crate 编译为 WebAssembly, - 在
.wasm
上运行wasm-bindgen
CLI 工具以生成其 JavaScript 接口, - 运行任何其他构建后工具,例如
wasm-snip
和wasm-opt
, - 整理你的 crate 或其 JavaScript 绑定可能需要的任何和所有 npm 依赖项,
- 并将生成的包发布到 npm。
所有这些都不需要你,作为 Rust 开发者,需要安装和运行 JavaScript 工具链。
现在,步骤 1、2 和 5 已到位,但你仍然需要在本地安装 npm
。对于 wasm-pack
还有更多计划,以及我们关于编排构建、依赖项和发布的故事即将发布,但你需要等待专门的后续博文。
等等,还有更多!
-
Twiggy 是一个用于
.wasm
二进制文件的代码大小分析器。 它可以帮助你回答诸如“为什么这个函数会出现在这里 - 谁调用它?”以及“如果我停止使用这个函数,将其删除,并删除所有在删除它之后成为死代码的函数,可以节省多少空间?”之类的问题。 -
wee_alloc
是一个为 WebAssembly 设计的微型分配器,其(压缩前)代码大小占用空间仅为 1 KB。 它面向进行少量初始动态大小分配,然后在没有进一步分配的情况下执行其繁重工作的代码。这种情况需要一些分配器存在,但我们很乐意以分配性能换取较小的代码大小。
-
The
console_error_panic_hook
crate provides a panic hook for wasm that logs panics to the developer console via theconsole.error
function. No more opaque “RuntimeError: unreachable executed” messages! Get the proper assertion failure message or index out-of-bounds information you expect. It makes debugging panics a whole lot easier. -
The
wasm-snip
tool lets you forcibly replace a function’s body with a singleunreachable
instruction. Maybe you know that some function will never be called at runtime, but the compiler can’t prove that at compile time? Snip it! Then run wasm-gc again and all the functions it transitively called (which could also never be called at runtime) will get removed too. This is particularly helpful for removing Rust’s panicking and formatting infrastructure when you intend to ship small.wasm
binaries withpanic=abort
.
即将到来:未来
正如本文中提到的,我们将发布更多博客文章,详细介绍我们对 Rust 2018 版本的具体目标以及您如何提供帮助。在此期间,请随时加入 Rust 和 WebAssembly 领域工作组,帮助构建 Rust 和 WebAssembly 的未来!