< 返回版块

Jonir Rings 发表于 2018-01-22 10:30

Tags:coordinate,rust,wasm

repo

本文为原Repo的Readme翻译。翻译时间:2018/01/22

Rust + WebAssembly: 初步整合

本Repo意在简单有机地整合Rust和WebAssembly的初步工作。

Key WebAssembly 背景

WebAssembly 是一种简单机器模型下的执行格式,其具有一个扩展规范

WebAssembly 并未和 JS 或者 Web 绑定;也没有预设其宿主环境。 因此也有理由假设 wasm 将会成为重要的跨环境“可移植执行体”格式。 可以这么说:今时今日,wasm 的确和 JS 关系重大,这是因为收到了多方的青睐(浏览器和 Node.js)。

WebAssembly 只有很小的一个值类型集合,基本上限制在简单数值的范围内。

WebAssembly 具有非常简单的内存模型。 目前,wasm 可以接入一个非常简单的“线性内存”,其基本上就是一块扁平的定长数字型数组。 这个内存可以以一个页大小的倍数增长,但不能缩减(shrink)。

状态

Rust 编译器

Rust 编译器目前支持两个 wasm 关联的目标(target):

  • wasm32-unknown-unknown。此目标直接使用 llvm 后端编译成 wasm。 它适合纯 rust 代码编译,譬如你没有 C 依赖的时候。 跟 emscripten 目标比起来,它默认就生成更加洗练的代码, 而且也便于设置搭建。此处查看如何设置搭建.

  • wasm32-unknown-emscripten。此目标利用 emscripten 工具链编译成 wasm。 当你具有 C 依赖的时候就得使用它了,包括 libc。此处查看如何设置搭建.

wasm32-unknown-unknown 十分有望将新生的 Rust 代码融入 JS 项目中。然而,它也是相对欠成熟的后端:

Rust 标准库

每个 wasm 目标对于 std 都有段不同的故事:

  • 对于 wasm32-unknown-unknown,Rust直接将自己那个很小的分配器用在 wasm 的页分配器上。 也就是说,所有 alloc 级别的 API(例如:所有的容器类型)都可用,而其他只存在于 std 上的 API —— 例如 thread,network,file,process —— 对于此目标都不可用。所有目前 API 虽然可见,但是 panic 和 error 其实都不可用。 我们计划将来将这些 API 为这个目标单独 cfg 出来。

    随着 wasm spec 的发展,这些附加的 API 可能会回来(特别是 thread)。

  • 对于 wasm32-unknown-emscripten,Rust 使用 emscripten 工具来来提供基于 libc 的功能。 也就是说,有大量的 std 可用,但是会导致明显的二进制文件膨胀。

JS 互操作

导入导出 JS 函数

从 Rust 侧角度

在 JS 宿主环境中运行 wasm 时,导入导出 Rust 函数十分直接:跟 C 的工作机制几乎一样。如下:

// 导入 JS 函数 `foo`
extern { fn foo(); }

// 导出 Rust 函数 `bar`
#[no_mangle]
pub extern fn bar() { /* ... */ }

因为 wasm 有限的值类型,这些函数只能操作原生数值类型。

从 JS 侧角度

JS 中的 wasm 二进制转换成 ES6 模块,其必须先使用一段线性内存和一组符合导入期望的 JS 函数来实例化。实例化详情请见 MDN

其生成的 ES6 模块将会包含所有 Rust 导出的函数,可以像 JS 函数一样使用。

是一个简单的案例,包含了整个设置。

在数值之上

当和 JS 一起使用 wasm 的时候,wasm 模块的内存和 JS 内存之间有明显的界限:

  • 每个 wasm 模块都有一段线性内存(本文档顶部有描述), 其在实例化期间初始化。JS 代码可以随意读写这段内存

  • 相反,wasm 代码无法直接接触 JS 对象。

因此产生了两种相当复杂互操作:

  • 将二进制数据拷入拷出 wasm 的内存。例如将一个 owned String 传给 Rust 侧。

  • 搭建一个显式的 JS 对象堆到给定“地址”。这使 wasm 可以简介引用 JS 对象(使用整数),然后使用导入的 JS 函数操作这些对象。

有幸的是,这种互操作使用一种“bindgen”风格框架(wasm-bindgen)也能经得住考验。这个框架使得可以编写常规的 Rust 函数签名,然后自动映射为常规的 JS函数。

JS 包生态

目前我们主要说的是函数层面的互操作,但实际生产中,我们经常也要与一级互操作,亦即是生成或者消费 npm 包。

此部分目前处于 设计阶段,有以下约束:

  • 基于 Rust/wasm 的包,其消费者无需意识到使用了 Rust。即是使用这种包的时候,需要本地 Rust 工具链。

    • 也就是以二进制的形式发布到 npm:我们上传一个 Rust 代码完全编译了的 .wasm 文件。
  • 你可以在库的 Rust 部分使用标准 Cargo 工作流来工作

  • 应该有种方式来描述 Rust/wasm 项目的 npm 元数据(如 package.json 的内容)。

    • 也就是说,Rust 项目可能会拉取自己的 crates,每个 crates 又会拉拉自己的 npm 依赖包。
  • 应该有某种方便的方式来发布这类项目到 npm,处理所有需要的相关依赖。

  • 最后,JS 打包器(例如 WebPackParcel)需要理解基于 wasm 的 npm 包,并生成合适的模块实例化。

如果你有意协助开发,请进入此 tracking issue!

DOM,GC 集成以及其他

目前存在一些困惑,wasm 代码是否能和 DOM 互通,或者这样会不会明显阻碍 GC 的集成。

需要澄清的是:wasm 当然能和 DOM 互通。你可以使用如同 wasm-bindgen 的策略操作 DOM 并通过 JS 调用回来。然而这样却引入了性能损失,因此通过批量处理 DOM 操作可以提升效率。譬如 changelist proposal 这种对 DOM 的提升,和 Host Bindings proposal 折中对 WebAssembly 的提升,将为 DOM 互通操作铺平道路。

crate 生态

crates.io 上已经有了 wasm 的初期生态,其中比较优秀的有:

  • stdweb,一个“客户端侧 web 标准库”。
  • Yew,客户端侧 web apps 框架。

Demo,演讲和其他

  • 多个 Rust 为中心的资源可以在找到 https://www.hellorust.com/ ,包含 demo,演讲,以及追踪关于 Rust 和 wasm 显著成就的新闻流。
  • 其外还有很多常规 wasm 资源:
    • http://webassembly.org/
    • https://github.com/mbasso/awesome-wasm
    • http://wasmweekly.news/

评论区

写评论
Mike Tang 2018-01-27 19:35

66666

@Jonir Rings 终于翻译完了……

作者 Jonir Rings 2018-01-27 16:40

终于翻译完了……

1 共 2 条评论, 1 页