【Rust 实战】Rust 与 Wasm

624 阅读5分钟

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.jsrust_wasm_bg.wasm 、rust_wasm_bg.d.ts 、rust_wasm.d.ts

rust_wasm.jsrust_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(56);
        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:rustwasm.github.io/ 

wasm-bindgen:github.com/rustwasm/wa…