什么是 Rust?
Rust 语言原本是 Mozilla 员工 Graydon Hoare 的私人项目,Mozilla 于 2009 年开始赞助这个项目,在 2010 年官方首次透露并于 2015 年发布 1.0 版本。在过去的十年中,编程语言 Rust 一直是一项突破性的技术,Rust 始终站在独特的学术研究和行业实用性结合的挑战视角。但是如果说 Rust 的影响仅仅是技术性的,那就错过了精髓,正如社区在 2016 年讨论的系列博客一样:"Rust 不仅是编程语言或编译器"和"Rust 让一切触手可及",同年 Rust 宣布了其官方口号:“一种让每个人都能够构建可靠和高效软件的语言”。
2021 年 2 月 8 日,Rust 基金会宣布成立,其基金会董事成员有:AWS、Google、华为、微软、Mozilla 。Rust 基金会诞生自 Rust 核心团队,并且得到了五位全球行业领先公司的财务承诺,这标志着 Rust 向成熟化迈出了坚实的一步。
为什么是 Rust?
性能
各种编程语言内存管理的方式不同,但通常有以下两种方式:
- 开发者自己分配和销毁: 比如 C、C++ 等,这种方式相当于把所有权力开放给开发者,管理不当容易内存泄漏。
- 编程语言提供自动垃圾回收机制: 比如 JavaScript、Java、Python 等,这种方式会产生运行时开销,对性能可能产生影响(注意这里是“可能”,没有办法证明性能一定比开发者自己管理要差)。
Rust 则另辟蹊径采用所有权、借用、生命周期机制在编译期自动插入内存释放逻辑来实现内存管理,由于没有了垃圾回收产生的运行时开销,Rust 整体表现的速度惊人且内存利用率极高。
fn main() {
let a = String::from("hello rust");
let b = a; // 所有权被转移
println!("{}", a); // 编译失败!a 已经被释放,无法再使用
}
在一项比较 REST API 性能的基准测试中(Rust 使用 Rocket,Node.js 使用 Restify),Rust 每秒处理 72,000 个请求,而 Node.js 为 8,000 个,空闲时使用的内存大约为 1MB,而 Node.js 为 19MB。 在另一个测试中(Rust 使用 Nickel,Node.js 使用 Restana),Rust 对请求的平均响应速度比 Node.js 快近 100 倍。
具体数据可以参考 Web Frameworks Benchmark。
可靠性
Rust 丰富的类型系统和所有权模型保证了内存安全和线程安全,让您在编译期就能够消除各种各样的错误,关于“内存安全”和“线程安全”的解释如下:
内存安全: 在具有内存安全性的编程语言中,所有内存访问都是明确定义的,通常内存不安全的情况包含:空指针、野指针、悬空指针、使用未初始化的指针、非法释放、缓冲区溢出、执行非法函数指针、数据竞争等。
据说微软 70% 的漏洞是内存安全问题
线程安全: 线程安全是程序设计中的术语,指某个函数、函数库在多线程环境中被调用时,能够正确地处理多个线程之间的共享变量,使程序功能正确完成。
生产力
Rust 拥有出色的文档、友好的编译器和清晰的错误提示信息, 还集成了一流的包管理器和构建工具, 智能地自动补全和类型检验的多编辑器支持, 以及自动格式化代码等等。
Rust 在前端构建工具中的应用
Deno
Github((80.9K star):https://github.com/denoland/deno
Deno 是一个简单、现代且安全的 JavaScript 和 TypeScript 运行时,它使用 V8 并基于 Rust 构建。Deno 是由 Node.JS 之父 Ryan Dahl 创建,在 2018 JS Conf Berlin 上借演讲《Design Mistakes in Node》首次对外公开。
Deno 诞生之初就是为了解决 Node 不安全和糟糕包的管理等老生常谈的问题,其中不安全时常令 Node.JS 开发者感到头疼和愤怒,近期也刚刚发生 node-ipc 事件(node-ipc 在所有用户的桌面上都会创建一个文件来宣传作者的政治观点),影响到了众多开源项目包括 Vue CLI 等。
因此 Deno 很自然地拥有以下特性:
-
默认安全,除非特别启用它,否则使用 Deno 运行的程序没有文件、网络或环境访问权限
deno run --allow-read mod.ts
-
开箱即用地支持 TypeScript
-
仅编译单个可执行文件
-
拥抱 Web 生态标准,内置了 fetch、localStorage、location 等 API
localStorage.setItem("myDemo", "Deno App"); // 拥有 10M 的持久化存储限制
-
内置依赖检查器
deno info
和代码格式化工具deno fmt
-
有一组经过审查的标准模块,可以与 Deno 一起使用:deno.land/std
目前已经有众多公司正在积极探索 Deno,包括 Amazon、Github、IBM、Vercel、Tencent、Microsoft 等头部技术公司。
SWC
Github(21K star):https://github.com/swc-project/swc
SWC 是一个可扩展的基于 Rust 的前端构建工具,目前核心功能相当于 Babel,包含以下这些模块:
模块 | 状态 | 作用 |
---|---|---|
@swc/cli | 稳定 | 该模块提供命令行入口 |
@swc/core | 稳定 | 该模块提供核心 SWC API |
@swc/wasm-web | 稳定 | 该模块允许使用 WebAssembly 在浏览器内同步转换代码 |
@swc/jest | 稳定 | ts-jest 的代替品,可以使单元测试速度有质的提升 |
swc-loader | 稳定 | 该模块允许将 SWC 与 Webpack 一起使用 |
swcpack | 建设中 | 该模块试图提供完整的构建器模块,可以简单理解为 Rust 版的 Rollup 或 Webpack |
官方提供的基准测试数据如下:
name | 1 core, sync | 4 promises | 100 promises |
---|---|---|---|
swc (es3) | 616 ops/sec | 1704 ops/sec | 2199 ops/sec |
swc (es2015) | 677 ops/sec | 1688 ops/sec | 1911 ops/sec |
swc (es2016) | 1963 ops/sec | 3948 op s/sec | 5580 ops/sec |
swc (es2017) | 1971 ops/sec | 3948 ops/sec | 6259 ops/sec |
swc (es2018) | 2555 ops/sec | 4884 ops/sec | 8108 ops/sec |
swc-optimize (es3) | 645 ops/sec | 1716 ops/sec | 1860 ops/sec |
babel (es5) | 34.05 ops/sec | 27.28 ops/sec | 32 ops/sec |
SWC 在单线程上比 Babel 快 20 倍,在四核上快 70 倍。
目前 SWC 已经被 Next.js、Parcel 和 Deno 等工具以及 Vercel、字节跳动、腾讯、Shopify 等公司广泛使用。
Parcel
Github(40k star):https://github.com/parcel-bundler/parcel
支持以 HTML 作为入口的零配置构建工具,Parcel 支持多种开箱即用的语言和文件类型,从 HTML、CSS 和 JavaScript 等 Web 技术到图像、字体、视频等资产。当您使用默认不包含的文件类型时,Parcel 将自动为您安装所有必要的插件和开发依赖项。Parcel 的 JavaScript 编译器和源映射是建立在 SWC 编译器之上的,在 SWC 之上,Parcel 实现了依赖项收集、捆绑、摇树优化、热重载等。
目前 Parcel 已经被广泛应用在微软、Atlassian、SourceGraph 等公司。
Rome
Github(17.2 star):https://github.com/rome/tools
Rome 是 Babel 作者做的基于 Node.js 的前端构建全家桶,包含但不限于 JavaScript、TypeScript、JSON、HTML、Markdown 和 CSS,在 2021 年 9 月 21 日 宣布计划使用 Rust 重构。
其它工具(WIP)
dprint:使用 Rust 编写,比 Prettier 快 30x 倍
postcss-rs:使用 Rust 编写,比 Postcss 快 20x 倍
Rust 在桌面应用开发中的应用
Tauri
Github(34.7 star):https://github.com/tauri-apps/tauri
在很长一段时间里,包括现在,Electron 都是最流行的跨平台桌面应用开发框架,目前在 Github 上有 101K 个 star,它允许你使用纯粹的前端技术(HTML、CSS、JS、Node.JS)来构建桌面应用,不过它也有两个比较明显的缺陷被人诟病:包体积太大和内存占用高,而造成这两个问题的根本原因是 Electron 是基于 Chromium 和 Node.JS 构建的。
Tauri 是 Electron 的代替品,现在 Tauri 试图去除 Chromium 转而使用 Rust 去和系统内置 Webview 进行绑定,简单来说就是在 Electon 时代你的应用永远使用 Chromium 内核,而在 Tauri 时代,你的应用在 Windows 上使用 Edge/Webview2,在 macOS 上使用 WebKit,在 Linux 上使用 WebKitGTK。基于 Rust 和 Webview 的好处很明显:包体积极小且内存占用极低。以下是官方提供的数据:
Detail | Tauri | Electron |
---|---|---|
Installer Size Linux | 3.1 MB | 52.1 MB |
Memory Consumption Linux | 180 MB | 462 MB |
Launch Time Linux | 0.39s | 0.80s |
Interface Service Provider | WRY | Chromium |
Backend Binding | Rust | Node.js (ECMAScript) |
Underlying Engine | Rust | V8 (C/C++) |
FLOSS | Yes | No |
Multithreading | Yes | Yes |
Bytecode Delivery | Yes | No |
Multiple Windows | Yes | Yes |
Auto Updater | Yes | Yes1 |
Custom App Icon | Yes | Yes |
Windows Binary | Yes | Yes |
MacOS Binary | Yes | Yes |
Linux Binary | Yes | Yes |
iOS Binary | Soon | No |
Android Binary | Soon | No |
Desktop Tray | Yes | Yes |
Sidecar Binaries | Yes |
看起来还不错的样子,不过别忘了如果你使用 Tauri 开发的话,后端(Electron 中叫主进程)目前只能使用 Rust,这将带来不小的学习成本,除此之外 Tauri 还有很长的路需要走,不过也算文艺复兴式的创新。
Rust 在 WebAssembly 中的应用
WebAssembly 是一种新的编码方式,具有紧凑的二进制格式,可以在现代的网络浏览器中以接近原生的性能运行,目前 Rust 和 WebAssembly 结合有两大主要用例:
-
整个 Web 应用都基于 Rust 开发:比如 Yew 等框架
-
在现存的 JavaScript 前端中使用 Rust
Yew
Github(Github 20k star):https://github.com/yewstack/yew
Yew 是一个设计先进的 Rust 框架,目的是使用 WebAssembly 来创建多线程的前端应用,它有几个特点:
- 基于组件的框架,可以轻松地创建交互式 UI。拥有 React 或 Elm 等框架经验开发人员在使用 Yew 时会感到得心应手。
- 高性能 ,前端开发者可以轻易地将工作分流至后端来减少 DOM API 的调用,从而达到异常出色的性能。(又是一个“文艺复兴式创新”...😂)
- 支持与 JavaScript 交互 ,允许开发者使用 npm 包,并与现有的 JavaScript 应用程序结合。
一个简单的 Yew 应用代码如下所示:
use yew::prelude::*;
enum Msg {
AddOne,
}
struct Model {
value: i64,
}
impl Component for Model {
type Message = Msg;
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self {
value: 0,
}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::AddOne => {
self.value += 1;
// the value has changed so we need to
// re-render for it to appear on the page
true
}
}
}
fn view(&self, ctx: &Context<Self>) -> Html {
// This gives us a component's "`Scope`" which allows us to send messages, etc to the component.
let link = ctx.link();
html! {
<div>
<button onclick={link.callback(|_| Msg::AddOne)}>{ "+1" }</button>
<p>{ self.value }</p>
</div>
}
}
}
fn main() {
yew::start_app::<Model>();
}
wasm-bindgen
Github(5.0k star):https://github.com/rustwasm/wasm-bindgen
目前 WebAssembly 类型系统还很小,只有四种数字类型,如果要使用复杂类型(例如字符串、对象、数组、结构体),需要花点心思:
- 将字符串或对象转换为 WebAssembly 模块可以理解的东西
- 将 WebAssembly 模块的返回值转换为 JavaScript 可以理解的字符串或对象
但是每次转换它们(序列化为线性内存,并提供它们所在位置的引用)是一项枯燥的工作并且容易出错,幸运的是,Rust world 想出了 wasm-bindgen
来促进 WebAssembly 模块和 JavaScript 之间的高级交互,其使用方式也非常简单:
-
创建一个 Rust 项目
$ cargo new --lib hello_world Created library `hello_world` package
-
打开
Cargo.toml
文件并添加wasm-bindgen
依赖项[package] name = "hello_world" version = "0.1.0" authors = ["Sendil Kumar <sendilkumarn@live.com>"] edition = "2018" [lib] crate-type = ["cdylib"] [dependencies] wasm-bindgen = "0.2.56"
-
打开
src/lib.rs
文件并将内容替换为以下内容use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn hello_world() -> String { "Hello World".to_string() }
-
编译成 wasm 模块
cargo build --target=wasm32-unknown-unknown
-
安装
wasm-bindgen-cli
,并将 wasm 文件转换成 JavaScript 文件cargo install wasm-bindgen-cli wasm-bindgen target/wasm32-unknown-unknown/debug/hello_world.wasm --out-dir . # ls -lrta # 76330 hello_world_bg.wasm # 1218 hello_world.js # 109 hello_world.d.ts # 190 hello_world_bg.d.ts
-
然后你就可以使用
hello_world.js
文件了,它可以帮你加载 wasm 文件。
wasm-pack
Github(4.1k star):https://github.com/rustwasm/wasm-pack
这是一个可以直接将你的 Rust 代码打包成 npm 包的工具,用法十分简单,只有 4 个命令:
new
:使用模板生成一个新的 Rust Wasm 项目build
: 从 rustwasm crate 生成一个 npm wasm pkgtest
:运行浏览器测试pack
和publish
:创建压缩包,发布到镜像仓库
值得注意的是,WebAssembly 目前还并不是提高 Web 应用性能的万金油,就目前来说,在 WebAssembly 中使用 DOM API 仍然比从 JavaScript 中调用要慢。但只是暂时性问题的,WebAssembly Interface Types 计划将解决这个问题。如果你想要了解更多关于这方面的信息,可以查看 Mozilla 的这篇文章 。
Rust 和 Node 的绑定
NAPI-RS
Github(2.2k star):https://github.com/napi-rs/napi-rs
NAPI-RS 是一个用于在 Rust 中构建预编译的 Node.js 插件的框架,SWC 便基于此库。
详细信息可以看《用 Rust 和 N-API 开发高性能 Node.js 扩展》这篇文章。
写在最后
随着前端开发复杂度的不断上升,配套工具链的效率将不容被忽视,受限于 Node.js 语言本身的效率问题,近几年将会有更多工具会被 Rust 重写,效率有望数倍乃至数十倍的提升,另外,Rust 和 WebAssembly 的结合也令人感到激动,但就目前来看离大规模上生产还有相当一段路需要走。
参考资料:
- https://vercel.app
- https://remix.run
- https://deno.com/deploy
- https://github.com/rustwasm/wasm-pack
- https://github.com/rustwasm/wasm-bindgen
- https://webassembly.org/
- https://github.com/yewstack/yew
- https://github.com/tauri-apps/tauri
- https://github.com/postcss-rs/postcss-rs
- https://github.com/devongovett/dprint-node
- https://rome.tools/
- https://parceljs.org/
- https://swc.rs/
- https://deno.land/
- https://web-frameworks-benchmark.netlify.app/compare?f=express,rocket
- https://rocket.rs/
- http://restify.com/
- https://github.com/nickel-org/nickel.rs
- https://github.com/BackendStack21/restana
- https://www.thefeedbackloop.xyz/safety-is-rusts-fireflower/
- https://github.com/graydon
- https://github.com/WebAssembly/interface-types/blob/main/proposals/interface-types/Explainer.md
- https://github.com/napi-rs/napi-rs
- https://nextjs.org/
- 《用 Rust 和 N-API 开发高性能 Node.js 扩展》:https://zhuanlan.zhihu.com/p/234914336