摘要

2019 年,Rust 和 WebAssembly 将从“可用”迈向“稳定、功能齐全、生产就绪”。

为了实现这一目标,Rust 和 WebAssembly 领域工作组将

  • 通过协作开发模块化工具包来培养库生态系统

  • 将多线程引入 Rust 生成的 Wasm

  • 将一流的调试支持集成到我们的工具链中

  • 完善我们的工具链和开发工作流程,最终发布 wasm-pack 的 1.0 版本

  • 投资监控、测试和分析基础设施,以确保我们的工具和库保持快速、稳定和生产就绪。

动机

这份路线图基于

详细说明

协作开发模块化工具包

以模块化方式构建 [高级库] 的想法非常令人兴奋,它将允许社区中的其他人以不同的方式将这些组件组合在一起。这有望使整个生态系统更加强大。

我特别希望看到以模块化方式实现带有 JSX 语法的虚拟 DOM 库的努力。在这方面已经有一些尝试,但它们似乎都比较单一且“功能齐全”。我希望这种情况在 2019 年有所改变。

— Ryan Levick 在 Rust WebAssembly 2019

不要创建品牌孤岛。品牌或许有助于获得知名度。但如果我们真正希望 Rust 的 Wasm 故事取得成功,我们应该考虑协作的方式,而不是划分地盘。

— Yoshua Wuyts 在 Wasm 2019

在 2018 年,我们创建了基础库,例如 js-sysweb-sys。在 2019 年,我们应该在它们的基础上构建模块化的高级库,并将这些库收集在一个伞形工具包箱中,以提供整体体验。这个工具包及其库将为针对 Wasm 的开发提供所有你想要的工具。

构建一个全新的 Web 应用程序?使用整个工具包快速上手。仔细制作一个小型 Wasm 模块并将其集成回现有的 JavaScript 项目?从工具包中获取你需要的那个目标库,并单独使用它。

  • 模块化:可以随意选择或放弃任何单个组件。优先考虑接口而不是实现。

  • 培养协作:我们已经在 Rust 和 WebAssembly 领域看到了生态系统的萌芽,以及许多伟大的实验,但我们还没有看到项目之间的大量协作。通过有意识地创造一个协作空间,我们可以减少重复工作,扩大影响,并帮助生态系统保持健康。

Wasm 的多线程

我们必须将 Rust 的 无畏并发 带到 Web 上!

— Nick Fitzgerald 在 Rust 和 WebAssembly 在 2019 年

在 WebAssembly 方面,我们必须走在最前沿,我们应该考虑成为第一个满足 [线程和原子操作] 的人。

— richardanaya 在 我的 Rust 2019 年梦想

我们的工具链已经 对 Wasm 中的多线程提供了实验性支持。浏览器目前正在发布 SharedArrayBuffer 和原子操作(Wasm 多线程的基元),这些操作在功能标志后面,预计将在 2019 年开始默认启用发布。

WebAssembly 的卖点之一是能够有效地利用可用硬件。多线程将这个故事从单个核心扩展到多个核心。虽然多线程对于 JavaScript 和任何编译到 Wasm 的语言来说都是实际上可行的,但 Rust 独特的拥有权系统使其在经济上是现实的

存在一些技术障碍(有关详细信息,请参阅上面的链接),这意味着我们无法让 Rust 标准库的 std::thread::* 在 Wasm 上运行。但重要的是,我们必须在整个生态系统中拥有线程池和锁等核心多线程构建块的共享实现。在 2019 年,我们应该将我们的实验性多线程支持转变为 Wasm 上多线程的生产就绪基础,让 rayon 等流行的箱子在 Web 上运行,并利用 Rust 的无畏并发。

调试

在 [调试] 正常工作之前(包括变量检查,目前在 wasm 中根本无法使用),其他一切都是玩玩而已。

— anlumo 在 r/rust 上的一条评论

拥有 [源代码映射] 将非常有利于调试。

— Yoshua Wuyts 在 Wasm 2019

调试很棘手,因为很大一部分工作不在这个工作组的掌控之中,它取决于 WebAssembly 标准化机构和实现浏览器开发者工具的人员。但是,我们可以采取一些具体的措施来改进调试

  1. println!dbg! 等在 Wasm 中开箱即用。为了实现这一点,我们将构建对 WebAssembly 参考系统根目录 和 Wasm 的标准系统调用的支持,这些系统调用正在标准化流程中。

  2. 创建将我们生成的 Rust Wasm 编译为带有源代码映射的 JavaScript 的能力,以便进行调试。源代码映射是一种有限的 JavaScript 调试信息格式,它允许在调试器中单步执行源代码位置,而不是单步执行编译生成的 JavaScript 代码。

  3. 将调试重点的跟踪和检测功能添加到我们的工具链中。例如,目前很难调试 Wasm 内存的 JavaScript 数组缓冲区视图被分离,因为 Wasm 内存已调整大小。我们可以通过可选地使用日志记录来检测 mem.grow 指令,从而使调试更容易。

除此之外,我们还应该与 WebAssembly 标准化机构和浏览器开发者工具制造商合作,积极参与 WebAssembly 调试子章程,在调试领域取得进展。通过保持环境和社会压力,并在我们力所能及的地方伸出援手,我们最终将拥有丰富的 Wasm 源代码级调试功能。

工具链和工作流程完善

设置 Wasm 项目需要相当多的样板代码。如果我们能找到减少这些代码的方法,那就太好了。

— Yoshua Wuyts 在 Wasm 2019

在 2018 年,我们打算在 wasm-pack 中包含一些功能,但最终没有实现。[...] 我们应该完成这些任务,并将 wasm-pack 打造成一个 1.0 工具。

— Nick Fitzgerald 在 Rust 和 WebAssembly 在 2019 年

在 2019 年,我们的工具链和工作流程应该功能齐全且经过完善。wasm-pack 作为我们工具链的入口点,将承担这项工作的重担,但其中大部分工作也将是在 wasm-pack 调用的工具中完成,而不是在 wasm-pack 本身中完成。

鉴于这项工作主要是关于填补空白和改善用户体验,所以它有点像一个清单。但这也是一个好兆头:这意味着 wasm-pack 实际上已经非常接近功能齐全了。

在我们完成所有这些任务后,我们应该发布 wasm-pack 的 1.0 版本。

监控、分析和测试基础设施

我在提出 rust/wasm 时遇到的主要反对意见是编译时间,但到目前为止,端到端延迟看起来相当有竞争力 [...] 在 CI 中设置一些基准测试,并在网上发布一个图表,将有助于保持这种状态。

— @jamii 在 RFC 评论

如果我想使用 libtest 和 wasm-bindgen-test 运行库的测试,我需要编写


# #![allow(unused_variables)]
#fn main() {
#[cfg_attr(not(target_arch = "wasm32"), test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn my_test() { ... }
#}

而不是仅仅编写


# #![allow(unused_variables)]
#fn main() {
#[test]`
fn my_test() { ... }
#}

— @gnzlbg 在 RFC 评论

我们应该构建类似于 perf.rust-lang.org 的基础设施0 来监控

  • 流行和基础 Wasm 库(例如我们模块化工具包中的那些库)的代码大小,以及

  • 我们 wasm-bindgenwasm-pack 的构建时间。

通过持续跟踪这些数据,我们将会对自己负责,确保提供轻量级工具包和“稳定、生产就绪”的工具链。

0 或者,如果合理且维护者愿意,我们可以将我们的监控集成到 perf.rust-lang.org 中。

这是宏观层面的基础设施故事,但我们也需要在微观层面上支持生态系统中库的需求。这意味着要继续投资于 Rust 生成的 Wasm 二进制文件的单元测试和性能分析。具体来说,我们应该

  • wasm-bindgen-test 中添加基准测试支持,以及

  • 使 wasm-bindgen-test自定义测试框架的 eRFC 向后兼容,为使常规的 #[test]#[bench] 在 Wasm 中“Just Work™”铺平道路,而不是要求使用 #[wasm_bindgen_test]

理由、缺点和替代方案

我们选择在 2019 年集中精力于

  1. 我们——Rust 和 WebAssembly 工作组——可以构建和发布的功能。我们不受外部因素阻碍的领域,例如仍在进行中的标准。

  2. 我们可以利用 WebAssembly 领域中Rust 独有的优势

我们可以构建和发布的东西

我们不想把命运掌握在别人手中,而是掌握在自己手中。

工具包和工具链的完善工作不涉及任何可能阻碍我们前进的外部实体。对于调试,虽然更大的故事涉及与外部群体和标准工作的重大共识,但我们明确选择专注于我们可以做的事情来改进我们自己的调试故事。我们不会让自己陷入 WebAssembly 社区组调试子章程所产生的任何东西的阻塞状态,也不会等待浏览器供应商在开发者工具中实现新的 Wasm 调试支持。

在路线图项目中,多线程故事风险最大:我们在该领域的成功取决于浏览器默认启用 Wasm 的多线程原语。然而,这似乎是一个相对安全的赌注,因为多线程原语已经过了最实验性的阶段,Chrome 已经默认启用它们,所有其他主要浏览器都有实现,只是还没有默认启用。

利用独特的优势

我们希望集中精力于我们能够最大程度地提高效率的地方,并确立自己在 WebAssembly 中的领导地位,这是其他人无法赶超的。

多线程故事也许是独特优势的最大例子:多线程臭名昭著地容易出现错误(至少可以说!),而 Rust 的所有权系统在编译时消除了数据竞争。

通过构建一个模块化的库工具包,我们增强了针对从插入到现有 JavaScript 应用程序中的微型模块到用 Rust 构建完整的 Web 应用程序的整个范围的能力。任何依赖于垃圾收集器、臃肿的运行时或对 FFI 和外部世界交互过于固执己见的语言都无法达到该范围的微型模块端。

工具链的完善和调试工作没有那么明显的独特优势。但两者都是良好开发体验的基础,而 Wasm 领域中这些事情的现状目前非常低,因此我们可以也应该从人群中脱颖而出。

已考虑的替代路线图项目

以下是一些在路线图中被考虑过的替代项目,也许是因为它们在 #RustWasm2019 帖子中被提及,但最终没有被包含。

anyref 集成到 Rust 语言中

我们已经做好了充分的准备,一旦 Wasm 中的宿主绑定和 GC 引用类型发布,就可以通过 wasm-bindgen 利用它们。我们可以更进一步,想象一个未来,Rust 语言能够以一种一等公民的方式传递对替代内存空间中对象的透明引用(其中一些可能是 GC 的):跨内存空间分割的结构体、指向多个内存空间的胖指针等等。

然而,目前尚不清楚将所有这些都推到语言中,与 wasm-bindgen 已经拥有的现有 anyref 在边缘”实现相比,会带来多少实用性。 此外,利用这项工作可能会很容易被阻碍:anyref 尚未在任何主流 Wasm 引擎中发布,并且将这种语言级别的集成通过更大的 Rust RFC 流程及其所有利益相关者进行,将会以蜗牛般的速度进行(如果它真的发生了!)。

只关注纯 Rust Web 应用程序

我们更愿意通过可以服务于从微型模块到整个 Web 应用程序的整个范围的模块化工具包来回答“是和”纯 Rust Web 应用程序,而不是只关注整个 Web 应用程序端。我们希望工具包能够让所有船只都受益,无论你的项目在该范围中的哪个位置。

此外,完整的 Web 应用程序并不是 Rust 的独特优势。JavaScript 一直在做这件事,就 Wasm 而言,在这个领域中有一些资金更充足的“竞争对手”,它们能够更快地提供更具说服力的单体 Web 应用程序开发体验(通过与工具、现有生态系统集成,或者投入资金和开发人员来解决问题)。微软和 Blazor、谷歌和 Go、使用 Emscripten 将现有的原生应用程序带到 Web 上等等。我们应该在最适合我们的地方竞争,而单体 Web 应用程序并不是那样。

尽管如此,如果你想用 Rust 生成的 Wasm 构建一个完整的 Web 应用程序,并且不想编写任何 JavaScript 代码,你应该能够做到。事实上,你已经可以使用 #[wasm_bindgen(start)]no-modules 目标来做到这一点。 我们永远不会删除这种能力,新的工具包只会让开发完整的 Web 应用程序变得更容易。

非 JavaScript 和非 Web 嵌入

虽然所有非 Web 和非 JavaScript WebAssembly 嵌入看起来都非常令人兴奋,但每个嵌入都是一个独特的环境,还没有一套标准的功能可用。我们不想等待一套完整的标准功能出现,也不想选择一个特定的嵌入环境。

我们确实打算支持参考系统根工作,以及之后出现的任何后续工作,但我们将根据机会利用这些东西,而不是将其作为路线图项目。

我们鼓励任何对非 JavaScript 和非 Web 嵌入感兴趣的人与 WebAssembly 社区组合作,通过定义标准的 Wasm 功能来推动这一故事向前发展!

未解决的问题

待定。