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 architecture

wasm-bindgen 如何工作?简而言之,它是一个过程宏,它接收带有 #[wasm_bindgen] 属性的 Rust 源代码,构建一个抽象语法树 (AST),然后它会生成两个工件

  1. 导入 JavaScript 对象并导出 Rust 对象的 Rust 绑定。

  2. 向其他 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 等服务。

wasm-pack cartoon

Lin Clark 在 让 WebAssembly 更好地为 Rust 和所有语言服务 中绘制的图

如果你是 Rust 开发者,并且想将编译为 wasm 的 crate 发布到 npm,wasm-pack

  1. 使用 wasm32-unknown-unknown 目标将 crate 编译为 WebAssembly,
  2. .wasm 上运行 wasm-bindgen CLI 工具以生成其 JavaScript 接口,
  3. 运行任何其他构建后工具,例如 wasm-snipwasm-opt
  4. 整理你的 crate 或其 JavaScript 绑定可能需要的任何和所有 npm 依赖项,
  5. 并将生成的包发布到 npm。

所有这些都不需要你,作为 Rust 开发者,需要安装和运行 JavaScript 工具链。

现在,步骤 1、2 和 5 已到位,但你仍然需要在本地安装 npm。对于 wasm-pack 还有更多计划,以及我们关于编排构建、依赖项和发布的故事即将发布,但你需要等待专门的后续博文。

等等,还有更多!

Twiggy!

Twiggy!

即将到来:未来

正如本文中提到的,我们将发布更多博客文章,详细介绍我们对 Rust 2018 版本的具体目标以及您如何提供帮助。在此期间,请随时加入 Rust 和 WebAssembly 领域工作组,帮助构建 Rust 和 WebAssembly 的未来!