0x00 开篇
这篇文章拖欠大家好久了,现在终于来了哈哈。本篇文章将介绍使用 Rust 来编译 Wasm 。另外,本篇文章还将告诉你为什么要选择 Rust 而不是 C++,Python 呢?如果你想提升Web的性能,又或者是想用JavaScript,Typescript以外的编程语言来编写前端,那么我建议你阅读本文章。
0x01 什么是Wasm?
Wasm 是 WebAssembly 的缩写。WebAssembly 是基于栈式虚拟机的二进制指令集,可以作为编程语言的编译目标,最初是在 _Web_上使用的,但由于它的特性及开放规范,目前它可以在很多场景下使用,比如:Node.js ,嵌入式程序等。目前大约有 40+ 的语言可以编写 Wasm。
0x02 Wasm 与 Web
目前我们常用的浏览器都可以运行 Wasm。那我们为什么要在 Web 上使用 Wasm 呢?
- 提升 Web 的性能
Wasm 是一种很容易解析的格式,它的目标就是充分发挥硬件的能力以达到原生执行效率。与 JavaScript 相比,使用 **Wasm **编写的程序可以拥有更好的性能。
- 方便移植
如果现有的软件是使用 C 或者是 C++ 等语言编写的,可以很方便的将编译为 Wasm,移植到 Web 端。
- 额外增加了 Web 端技能
如果你不会 JavaScript,依然可以使用其它可以编译成 Wasm 的语言来开发 Web 端,额外为自己增加了一项技能。类似的框架有 Yew,感兴趣的可以了解下。
0x03 为什么选择 Rust?
Rust 内存效率高,没有运行时,没有垃圾回收器。并且 Rust 的可靠性也很高,在编译时期可以确保大部分的内存和线程安全。与其他高级语言相比,Rust 中的编程虽然更加复杂,但是 Rust 是一种系统级编程语言,我们性能和复杂性不可兼得,只能在保证性能的同时忽略其复杂性。当然,又有人会问为什么不选择 C 或者 C++ 呢?C 或者 C++ 也同样可以实现上面的性能。最重要的原因也是我上面所说到的,C 和 C++ 没有保证内存和线程的安全,一旦发生内存级别的错误,调试起来非常痛苦,这无疑是增加了维护成本。另外像 Python 等高级语言的缺点也很明显,在性能方面很难达到合格的标准。
0x04 实战演练
PS:演示没有选择使用命令创建项目,接下来的演示将以 Windows 11 操作系统 x64 和 CLion 2022.2 版本进行演示。演示过程没有采用 CLion 的 WebAssembly 模板。
1 创建项目
创建项目时选择 Libray 模板,并删除模板创建 lib.rs
的所有代码
2 编写代码
写一个简单的代码,计算两个整数的和。首先我们配置下 Cargo.toml
。在 lib 标签下添加构建类型为 cdylib。添加 wasm-bindgen 依赖,该依赖是生成 wasm 的关键依赖。
[package] name = "rust_wasm" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] crate-type = ["cdylib"] [dependencies] wasm-bindgen = "0.2.82"
在 lib.rs
中编写代码
use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn sum(a: i32, b: i32) -> i32 { return a + b; }
3 编译代码
下载编译工具 wasm-pack
。文末附下载地址。
然后使用下面的命令编译代码,下面这行代码首次执行,当执行到 **Installing wasm-bindgen...**时会等很久,大约5-10分钟左右。
wasm-pack build --target web
PS:如果5-10分钟后,编译还卡在 Installing wasm-bindgen... 。有两种解决办法,挂梯子或者更换 Cargo 源为国内源。
以 Windows 为例,找到 Cargo 默认的安装位置 C:\Users\你自己的用户名\.cargo
,如果目录下存在名为 config
的文件就直接打开,如果没有就创建一个新的 config
文件。输入以下内容并保存。
[source.crates-io] registry = "https://github.com/rust-lang/crates.io-index" replace-with = 'ustc' [source.ustc] registry = "http://mirrors.ustc.edu.cn/crates.io-index"
成功安装 wasm-bindgen
后,你可能还会遇到这个 warning。
warning: be sure to add `C:\Users\xxx\AppData\Local\.wasm-pack\.wasm-bindgen-cargo-install-0.2.82\bin` to your PATH to be able to run the installed binaries
我们按照提示将上面的路径添加到环境变量里,重新编译就可以了。如果编译成功,在项目目录下会生成 pkg
文件夹,里面的内容就是生成的 wasm 相关文件了。
主要有四个重要的文件——rust_wasm.js
、rust_wasm_bg.wasm
、rust_wasm_bg.d.ts
、rust_wasm.d.ts
。
rust_wasm.js
、rust_wasm_bg.wasm
就是我们所需的 wasm 和 js 的胶水代码。
rust_wasm_bg.d.ts
、rust_wasm.d.ts
则是 Typescript 类型的定义。
4 测试代码
我们写一个简单的 html 文件测试下 wasm 模块。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Rust Wasm 测试</title> </head> <script type="module"> import init, {sum} from "./pkg/rust_wasm.js"; init().then(() => { var result = sum(5, 6); document.write("wasm sum 5 + 6 = " + result) console.log(result); }); </script> <body> </body> </html>
我们直接打开浏览器运行会报跨域的错误。我们要么把文件拷到服务器上,要么就在本地跑一个服务器。测试结果如下:
0x05 小结
上面讲到编译过程中会卡住的问题,我感觉理论上可以本地安装wasm-bindgen和wasm-pack这俩工具,但是我并没有尝试,喜欢折腾的小伙伴可以尝试下。Rust 编译为 Wasm 的教程到这里基本就结束了。当然这也仅仅是简单的生成 Wasm。当然如果你感兴趣,我会找时间再来说下 Rust 与 JavaScript 的交互以及如何操作 DOM。另外,其实很多我们熟知的公司也都在使用 Rust 生成的 wasm,比如迪士尼的流媒体服务和亚马逊的流媒体服务等等。
0x06 其它资料
wasm-pack:https://rustwasm.github.io/
wasm-bindgen:https://github.com/rustwasm/wasm-bindgen