• 前端视角对Rust的浅析


    概述

    本文将从 Rust 的历史,前端的使用场景和业界使用案例一步步带你走进 Rust的世界。并且通过一些简单的例子,了解 Rust 如何应用到前端,提高前端的生产效率。

    Rust简史

    2006年,软件开发者Graydon HoareMozilla工作期间,开始了Rust作为一个个人项目。根据他在麻省理工技术评论的一次采访,Rust的灵感来自于Hoare公寓楼里一个坏掉的电梯。电梯操作系统的软件崩溃了,Hoare意识到这类问题通常源于程序如何使用内存的问题

    通常,这些类型设备的软件是用CC++编写的,但这些语言需要大量的内存管理,可能导致系统崩溃的错误。因此,Hoare着手研究如何创建一种既紧凑又无内存错误的编程语言

    Mozilla 于2009年开始赞助这个项目,并且在2010年首次公开。也在同一年,其编译器源代码开始由原本的 OCaml 语言转移到用 Rust 语言,进行自我编译工作,称作“rustc”,并于2011年实际完成。这个可自我编译的编译器在架构上采用了 LLVM 作为它的后端。

    第一个有版本号的Rust编译器于2012年1月发布。Rust 1.0是第一个稳定版本,于2015年5月15日发布。

    2021年2月8日,AWS、华为、Google、微软以及 Mozilla 宣布成立Rust基金会,并承诺在两年时间里每年投入不少于 100 万美元的预算,以用于 Rust 项目的开发、维护和推广

    根据Rust 最新官方新闻,谷歌日前宣布向 Rust 基金会捐款 100 万美元,这笔资金将用于改善 C++ 与 Rust 互操作性。谷歌当前正在使用 Rust 语言重写在 Linux 核心之外的 Android 关键安全组件,从而进一步减少安全漏洞。而在 Android 13 中,就已经有约 21%的新原生代码使用 Rust 语言开发

    Rust在前端能够干什么?

    • SWC: 基于 Rust 的前端构建工具,可以理解为 Rust 版本的 Babel,但是性能有 10 倍提升。目前被 Next.js、Deno , Rspack等使用。

    • Tauri:Tauri 是目前最流行的 Electron 替代方案,通过使用 Rust 和 Webview2 成功解决了 Electron 的包体积大和内存占用高的问题。Atom 团队也是看到了 Tauri 的成功,才决定基于 Rust 去做 Zed 编辑器。

    • Parcel2:零配置构建工具,特点是快速编译和不需要配置,和 Vite、Webpack等打包比起来更加简单,而且是基于 Rust 开发

    • Biome: 旨在取代许多现有的 JavaScript 工具,集代码检测、打包、编译、测试等功能于一身。

    • Rspack: 基于 Rust 的高性能 Web 构建工具, 对标 Webpack, 兼容大部分Webpack api

    • Rocket: 可以帮助开发人员轻松编写安全的Web应用程序, 对标 Expressjs,性能卓越,具体参考 Web Frameworks Benchmark

    • Yew: 使用 Rust 开发 h5 页面,支持类 jsx 的语法,和 React 类似开发前端网页,打包产物是 wasm,挺有趣。

    • Napi-rs: 用 Rust 和 N-API 开发高性能 Node.js 扩展,可以替代之前用 C++ 开发的 Node.js 扩展,许多基于 Rust 语言开发的前端应用都结合这个库进行使用。

    Rust为什么大受欢迎

    • 高性能:所有程序都必须管理其运行时使用计算机内存的方式。一些语言(比如:JavaScript )中具有垃圾回收机制,在程序运行时不断地寻找不再使用的内存,在另一些语言中,程序员必须亲自分配和释放内存。Rust 则选择了第三种方式:通过所有权系统管理内存,编译器在编译时会根据一系列的规则进行检查。如果违反了任何这些规则,程序都不能编译。正是这种内存管理机制,使得 Rust 有惊人的内存利用率。

    • 内存安全:Rust 丰富的类型系统和所有权模型保证了内存安全,让你在编译期就能够消除各种各样的错误。Rust 通过所有权系统管理内存,编译器在编译时会根据一系列的规则进行检查,如果违反了任何这些规则,程序都不能编译。

    • 线程安全:Rust 通过一整套基础设施和类型检查,强迫这些线程问题暴露在编译阶段,相比花费大量时间尝试重现运行时并发 bug 出现的特定情况,Rust 会拒绝编译不正确的代码并提供解释问题的错误信息。

    同时大量头部公司比如华为,微软,字节的应用和推广,使得rust快速占领着前端基础设施领域。Webpack、Babel、Prettier 这些热门工具都已有了 Rust 替代方案,且性能有着 10~100 倍的提升。任何能够用 Rust 实现的应用系统,最终都必将用 Rust 实现。

    Rust 缺点

    1. 学习曲线陡峭:Rust 拥有复杂的语法和严格的规则,对于初学者来说可能会感到困难和挑战。

    2. 编译时间长:由于 Rust 的编译器进行了大量的静态检查和优化,编译时间可能相对较长,特别是对于大型项目。希望未来 Rust 针对这块做出更多优化

    3. 生态系统相对不太完善:相比其他编程语言,Rust 的生态系统相对较小,可能缺乏一些常见的库和工具。

    4. 错误处理繁琐:Rust 采用了 Result 和 Option 等类型来处理错误和空值,这要求开发人员进行显式的错误处理,导致一些额外的编码工作量。

    Rust 生产实践优秀案例

    Rust语言在IM客户端的实践 (https://juejin.cn/post/7336022842856177690)

    结合了 Rust 语言的优点,成功地解决高并发接待 & 多开。

    给 Web 前端工程师看的用 Rust 开发 wasm 组件实战(https://juejin.cn/post/7308434321764794378)

    利用 Rust 生成的wasm,处理大量计算的场景,取得很好的收益。(https://juejin.cn/post/7303347466219569203)

    Bundler 的设计取舍:为什么要开发 Rspack?

    文章作者通过使用了 Webpack、Vite、Esbuild、Rollup 等构建工具,对各个工具的优劣处和设计取舍后决定采用rust提升构建工具的性能。

    通过上面几个案例我们可以看到,对于前端我们前端来说,利用 Rust 可以做一些计算量比较大wasm和一些构建工具相关的基础设施收益还是比较大的。当然也可以做跨端应用,可以参考用 Rust 实现跨平台开发(iOS/Android/Web)经验分享(https://zhuanlan.zhihu.com/p/677550790)

    Rust 在 WebAssembly 中的应用

    WebAssembly 是什么?

    WebAssembly 是一门不同于 JavaScript 的语言,WebAssembly 是一门低级的类汇编语言。它有一种紧凑的二进制格式,使其能够以接近原生性能的速度运行,并且为诸如 C++ 和 Rust 等拥有低级的内存模型语言提供了一个编译目标以便它们能够在网络上运行。

    wasm二进制内容如下图

    cb9efbe79dd94eb0d7e6cc37297db4bb.png

    如何生成 WebAssembly?

    • 使用 Emscripten 移植一个 C/C++ 应用程序。

    • 直接在汇编层,编写或生成 WebAssembly 代码。

    • 编写 Rust 程序,将 WebAssembly 作为它的输出。

    • 使用 AssemblyScript,它类似于 TypeScript 并且可编译成二进制 WebAssmebly 代码

    由于 Rust 生态天然支持 WebAssembly,并且有专门的生态去跟踪和优化WebAssembly,我们可以利用 Wasm-Pack 脚手架生成 Wasm 项目。

    首先安装 Rust 相关的环境,和 wasm-pack 脚手架

    这是一个可以直接将你的 Rust 代码打包成 Npm 包的工具,用法十分简单,只有 4 个命令:

    • new:使用模板生成一个新的 Rust Wasm 项目

    • build: 从 Rust Wasm Crate 生成一个 Npm Wasm Pkg

    • test:运行浏览器测试

    • packpublish:创建压缩包,发布到镜像仓库

    ac8d6f5cdcc913d0f12f11d18666f90f.png
    img

    Pkg hello_wasm.js 就是最终wasm 入口文件,

    html中异步引入

    1. const { default: init } = await import("./pkg/hello_wasm.js");
    2. const res = await init();

    异步引入 hello_wasm.js 后,会自动加载 hello_wasm_bg.wasm 二进制文件,res 可以获取从二进制得到的变量。间接从获得了从 Rust 语言到 Js 传递,当然 Rust 也可以调用 Js 的方法,可以通过

    #[wasm_bindgen(js_namespace = console)]

    通过在 Rust 函数或结构体上使用 #[wasm_bindgen] 属性和相应的配置,我们可以将 Rust 代码暴露给 JavaScript 使用,以实现跨语言的交互和调用。在这种情况下,#[wasm_bindgen(js_namespace = console)] 指定了 JavaScript 中的 console 对象的命名空间,使得我们可以使用类似于 

    console.log() 的方式在 Rust 中输出到 JavaScript 的控制台。

    Rust 和 Node 的绑定

    哪些功能适合用 Native Addon 来完成

    • 简单的输入输出但是中间逻辑复杂的计算逻辑,比如直接用到 CPU simd 指令的 @node-rs/crc32 , 或者加密算法 @node-rs/bcrypt, 中文分词 @node-rs/jieba 。这些库的逻辑都有一个共同点:输入输出都非常简单(避免额外的 N-API 调用), 中间计算逻辑非常复杂。

    • 一些需要调用系统级 API 能力的库,比如提到的 SIMD 指令,还有类似 GPU 调用等。

    社区已经有成熟的 Napi-rs 来封装 Native Addon。

    首先通过安装 Napi-rs 脚手架生成项目

    d47ed353e8347f9090c9b9fe501934c6.png
    img

    生成的项目大概长这种模样。通过执行 npm run build 我们可以生成 index.js和 napi-demo.darwin-arm64.node 二进制文件。

    const demo = require("./index.js");

    引入 index.js 就可以获取到 Rust lib.rs 中暴露的变量和方法了。其实像 Swc,Rspack 这些重计算的工具底层也是通过此库进行 Rust 语言和 Nodejs 进行交互的。Rust 提供一些核心底层能力,Node 可以结合现有的生态调用经过 Rust生成二进制文件创造更多性能卓越的应用。

    总结

    随着前端开发越来越卷,前端基建的效率也将是下一个值得投入的地方。并且随着 Rust 和 WebAssembly 发展,网页应用也有很大的发展空间。最后希望在深入学习 Rust 后,做出一些有意思的应用。

    附录

    https://zhuanlan.zhihu.com/p/101118828

    https://developer.mozilla.org/zh-CN/docs/WebAssembly/Rust_to_wasm

    https://zhuanlan.zhihu.com/p/234914336

    https://www.zhihu.com/question/603518666/answer/3256663127

    https://juejin.cn/post/7336022842856177690

    https://juejin.cn/post/7317854227748847616

    https://zhuanlan.zhihu.com/p/677550790

    https://zh.wikipedia.org/zh-cn/Rust

    https://juejin.cn/post/7076354498691596325

  • 相关阅读:
    为 VitePress 网站添加 RSS 订阅支持
    解析java中的StringBuffer和StringBuilder
    产品经理必备知识——API接口
    探针配置失误,线上容器应用异常死锁后,kubernetes集群未及时响应自愈重启容器?
    多线程案例(线程池篇)
    kafka为什么性能这么高?
    PyTorch 张量数据类型
    Java设计模式总结
    轻松掌握组件启动之MongoDB(番外篇):高可用复制集架构环境搭建-mtools
    Mybatis的sql语句执行异常后打印到日志
  • 原文地址:https://blog.csdn.net/P6P7qsW6ua47A2Sb/article/details/136334635