CC's

Back

更新于 2024-03-01。主要是 wasm-pack-cli 被弃用。

WASM 是一种虚拟机上的二进制格式,不限语言,跨平台,能够运行在 Web 页面上。相对于 js 而言,WASM 的效率更高,更适合处理高计算量任务。至于为什么会有在浏览器做高计算量的工作…你总会找到场景的,压缩、渲染、游戏计算……

Rust 是对 WASM 支持良好的语言之一,并且从 Rust 本身继承了不少的好处:

  • Predictable performance
  • Small code size
  • Modern amenities

具体可参见 https://www.rust-lang.org/what/wasm

在本文中我们将简单入门和使用 Rust 编写一个娱乐性的项目,并编译为 WASM 部署到网页上。前置知识将尽量限制于 Rust,不会过多涉及 Node.js。

配置环境#

建议直接使用 rustup 完成这一步:

$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
bash

基本是自动的,不多做介绍。详细步骤和特殊需求可以参考官方文档。

而后我们需要安装 wasm 的 target 和 wasm-pack。

$ rustup target add wasm32-unknown-unknown
$ cargo install wasm-pack
$ cargo install cargo-generate
bash

Rust 的环境配置到此就结束了。一般情况而言,你可能还想继续配置 Node.js 一侧的环境,但这已经超出了本文内容。下文中所有的操作都不需要 node 环境。

创建项目#

我们准备写一个简单的娱乐项目,仅作为学习使用。这个小程序能够把输入的文本使用语气词编码和译码。大概效果就像:

# 输入
已经没有什么可怕的了。

# 编码结果
呼!啊啊~呜..啊啊!呼~呜呜..呼..呜呜!呜呜!呜呜!咿!唔..咿啊..啊..咿~啊嗯!唔..呜啊..嗯啊!咿呜..呼啊..咿呜~呼~咿..啊嗯~嗯啊!咿呜..咿呜..呜嗯..唔!咿!啊啊..啊嗯!咿呜..啊..啊~啊嗯!哈啊..呼啊..啊嗯~嗯!呼!咿啊~啊~哈啊!啊啊!呼~咿啊..呜..呜嗯!啊嗯~啊啊!啊啊~呜..嗯!呼啊!啊啊..咿呜!呜呜!呜!呼~呜啊..唔!呜嗯!呜~呜啊..
bash

我们创建一个 lib 项目,并删除默认的 Cargo.toml 内容,写入给 core lib 和 wasm binding 预留的 workspace。

$ cargo generate --git https://github.com/rustwasm/wasm-pack-template
bash
[workspace]
members=[
    "modal_particles",
    "modal_particles_wasm",
]
toml
$ cargo new modal_particles --lib
$ cargo new modal_particles_wasm --lib
bash

我们将在 modal particles 里编写主要逻辑,然后在 modal particles wasm 里编写预留给 WASM 的 API。之后如果有兴趣,可以方便地在这基础上继续增加 CLI 等。

modal_particles_wasmCargo.toml 配置如下内容:

在正式开始之前,切换到 modal_particles_wasm 目录,编译测试一下环境是否配置正确:

# 如果你准备将编译生成的 WASM 使用在 NPM 项目中,那就不要加 `--target web`
$ wasm-pack build --target web
bash

理想情况下将在同目录中生成 pkg 文件夹。

基本思路#

我们的想法非常简单:把输入使用 UTF-8 编码,将 byte 按照一定规则对应到多个语气词上。译码时反向映射即可。

在思来想去之后我找到了 16 个还算合适的语气词,能够编码一个 4 位 bit。

  • 在把输入编码为 UTF-8 后,我们把一个 byte b 拆分成 b&0xF b>>4&0xF 2 部分,分别对应到 16 个语气词上。因为选词时并没有考虑前缀重复的问题,所以继续在每个映射结果间插入一些间隔符号,以方便在译码时作为划分依据。
  • 译码时先对输入划分,然后查询相邻两个语气词对应的编号 a b,并按照 a|b<<4 拼起来就得到了原始的 UTF-8 编码。

编码的主要部分如下,没什么可说的,就是简单的查表输出……

同样流式地实现译码过程,节省内存。因为有着 2 对 1 的关系,需要暂存没能解码完全的内容,所以解码器需要一个 buffer。其他也没什么可说的……

除此之外其他部分就都是比较普通的代码了。完成 modal_particles 的编写后,在 wasm 里封装为编码和译码两个 API encodedecode

编译 & 使用#

运行

$ wasm-pack build --target web
bash

目录下将生成 pkg 文件夹,内部有如下文件:

modal_particles_wasm.js
modal_particles_wasm.d.ts
modal_particles_wasm_bg.wasm
modal_particles_wasm_bg.wasm.d.ts
package.json
bash

关于这几个文件其实有一些可说道的。

  • package.json 是给 NPM 包使用的,因为我们指定编译为网页可使用的代码,所以不必管这个东西。
  • 在理想情况下,应该可以直接 import * from <a wasm file>
  • 可惜事实并没有这么美好,所以 wasm-pack 准备了简单的 js 封装以暴露我们在 Rust 中事先定义的接口。

在 HTML 里用以下代码就能加载这个 WASM 并使用了。

<script type="module">
    import init,{encode,decode} from "modal_particles_wasm.js";
    async function run(){
        // Init first before using other methods.
        await init();
        // Use the exported API...
    }
    run();
</script>
html

试一试:语”气”

用 Rust 玩一玩 WASM
https://astro-pure.js.org/blog/hello-world-to-rust-wasm
Author Cheng Chen
Published at 2023年6月24日
Comment seems to stuck. Try to refresh?✨