Rust 入门(五):Rust + WebAssembly 实战

January, 11th 2026 9 min read Markdown
Rust 入门(五):Rust + WebAssembly 实战

Rust 入门系列(第 5 篇,共 5 篇 - 完结篇) 前置阅读第 4 篇 - 实战 CLI 工具 阅读时间:25 分钟


引言

WebAssembly(WASM) 是一种可以在浏览器中运行的二进制格式,性能接近原生代码。Rust 是编译到 WASM 最成熟的语言之一。

这篇文章会教你:

  • ✅ 什么是 WebAssembly
  • ✅ 为什么 Rust + WASM 是最佳组合
  • ✅ 如何用 wasm-pack 构建 WASM 项目
  • ✅ 实战:构建一个图像处理模块

一、WebAssembly 简介

1.1 什么是 WebAssembly?

MDN 定义

WebAssembly is a type of code that can be run in modern web browsers — it is a low-level assembly-like language with a compact binary format.

关键特性

  • 🚀 高性能:接近原生速度
  • 🌐 跨平台:浏览器、Node.js、服务端
  • 🔒 安全:沙箱环境运行
  • 📦 体积小:二进制格式,压缩后更小

1.2 为什么用 Rust 编译 WASM?

根据 Rust 官方文档

  1. 无 GC:无垃圾回收,性能可预测
  2. 小体积:编译出的 WASM 模块很小
  3. 工具链成熟:wasm-pack、wasm-bindgen 等工具完善
  4. 类型安全:编译时检查,减少运行时错误

真实案例

  • Figma:渲染引擎
  • Photoshop Web:图像处理
  • Google Earth:3D 渲染

二、环境搭建

2.1 安装 wasm-pack

官方安装指南

bash
1
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

验证安装

bash
1
wasm-pack --version

2.2 创建项目

bash
12
cargo new --lib hello_wasm
cd hello_wasm

2.3 配置 Cargo.toml

toml
12345678910
[package]
name = "hello_wasm"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2"

三、Hello World

3.1 编写 Rust 代码

rust
1234567
// src/lib.rs
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn greet(name: &str) -> String {
    format!("Hello, {}!", name)
}

3.2 编译

bash
1
wasm-pack build --target web

输出

plaintext
12345
pkg/
├── hello_wasm_bg.wasm         # WASM 二进制文件
├── hello_wasm.js              # JavaScript 绑定
├── hello_wasm.d.ts            # TypeScript 类型定义
└── package.json

3.3 在浏览器中使用

创建 index.html

html
123456789101112131415161718192021
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello WASM</title>
</head>
<body>
    <h1>Hello WebAssembly!</h1>
    <script type="module">
        import init, { greet } from './pkg/hello_wasm.js';

        async function run() {
            await init();
            const result = greet('World');
            document.body.innerHTML += `<p>${result}</p>`;
        }

        run();
    </script>
</body>
</html>

启动本地服务器

bash
12345
# Python
python3 -m http.server 8000

# Node.js
npx serve

访问 http://localhost:8000,看到 “Hello, World!”。


四、实战:图像滤镜

4.1 项目目标

用 Rust 实现图像灰度化处理,性能远超 JavaScript。

4.2 添加依赖

toml
123
[dependencies]
wasm-bindgen = "0.2"
image = "0.24"

4.3 Rust 代码

rust
1234567891011121314151617181920212223242526272829303132333435363738
use wasm_bindgen::prelude::*;
use image::{ImageBuffer, Rgba};

#[wasm_bindgen]
pub struct Image {
    width: u32,
    height: u32,
    data: Vec<u8>,
}

#[wasm_bindgen]
impl Image {
    #[wasm_bindgen(constructor)]
    pub fn new(width: u32, height: u32, data: Vec<u8>) -> Image {
        Image { width, height, data }
    }

    pub fn grayscale(&self) -> Vec<u8> {
        let mut result = Vec::with_capacity(self.data.len());

        for chunk in self.data.chunks(4) {
            let r = chunk[0] as f32;
            let g = chunk[1] as f32;
            let b = chunk[2] as f32;
            let a = chunk[3];

            // 灰度公式
            let gray = (0.299 * r + 0.587 * g + 0.114 * b) as u8;

            result.push(gray);
            result.push(gray);
            result.push(gray);
            result.push(a);
        }

        result
    }
}

4.4 JavaScript 调用

html
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Image Filter</title>
</head>
<body>
    <h1>图像灰度化</h1>
    <input type="file" id="upload" accept="image/*">
    <br>
    <canvas id="original"></canvas>
    <canvas id="filtered"></canvas>

    <script type="module">
        import init, { Image } from './pkg/hello_wasm.js';

        await init();

        const upload = document.getElementById('upload');
        const originalCanvas = document.getElementById('original');
        const filteredCanvas = document.getElementById('filtered');

        upload.addEventListener('change', async (e) => {
            const file = e.target.files[0];
            const bitmap = await createImageBitmap(file);

            // 绘制原图
            originalCanvas.width = bitmap.width;
            originalCanvas.height = bitmap.height;
            const originalCtx = originalCanvas.getContext('2d');
            originalCtx.drawImage(bitmap, 0, 0);

            // 获取像素数据
            const imageData = originalCtx.getImageData(0, 0, bitmap.width, bitmap.height);

            // Rust 处理
            const img = new Image(bitmap.width, bitmap.height, Array.from(imageData.data));
            const grayData = img.grayscale();

            // 绘制灰度图
            filteredCanvas.width = bitmap.width;
            filteredCanvas.height = bitmap.height;
            const filteredCtx = filteredCanvas.getContext('2d');
            const newImageData = new ImageData(
                new Uint8ClampedArray(grayData),
                bitmap.width,
                bitmap.height
            );
            filteredCtx.putImageData(newImageData, 0, 0);
        });
    </script>
</body>
</html>

五、性能对比

5.1 JavaScript 版本

javascript
12345678910
function grayscaleJS(imageData) {
    const data = imageData.data;
    for (let i = 0; i < data.length; i += 4) {
        const gray = 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2];
        data[i] = gray;
        data[i + 1] = gray;
        data[i + 2] = gray;
    }
    return imageData;
}

5.2 性能测试

javascript
123456789
// JavaScript
console.time('JS');
grayscaleJS(imageData);
console.timeEnd('JS');

// Rust WASM
console.time('WASM');
const grayData = img.grayscale();
console.timeEnd('WASM');

结果(1920x1080 图像)

  • JavaScript:~50ms
  • Rust WASM:~10ms
  • 性能提升:5x

六、与 JavaScript 互操作

6.1 传递复杂数据

rust
1234567891011121314
use wasm_bindgen::prelude::*;
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
pub struct User {
    name: String,
    age: u32,
}

#[wasm_bindgen]
pub fn process_user(json: &str) -> String {
    let user: User = serde_json::from_str(json).unwrap();
    format!("{} is {} years old", user.name, user.age)
}
javascript
12345
import { process_user } from './pkg/hello_wasm.js';

const user = { name: 'Alice', age: 30 };
const result = process_user(JSON.stringify(user));
console.log(result); // "Alice is 30 years old"

6.2 从 Rust 调用 JavaScript

rust
1234567891011121314151617
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = console)]
    fn log(s: &str);

    #[wasm_bindgen(js_namespace = Math)]
    fn random() -> f64;
}

#[wasm_bindgen]
pub fn generate_random() -> i32 {
    let r = random();
    log(&format!("Random: {}", r));
    (r * 100.0) as i32
}

七、优化技巧

7.1 减小体积

bash
12345
# 优化编译
wasm-pack build --release

# 使用 wasm-opt
wasm-opt -Oz -o output.wasm input.wasm

7.2 延迟加载

javascript
123456
// 按需加载
button.addEventListener('click', async () => {
    const wasm = await import('./pkg/heavy_module.js');
    await wasm.default();
    wasm.heavy_computation();
});

7.3 Worker 中运行

javascript
1234567
// worker.js
importScripts('./pkg/hello_wasm.js');

self.addEventListener('message', async (e) => {
    const result = process_data(e.data);
    self.postMessage(result);
});

八、实际应用场景

8.1 适合 WASM 的场景

CPU 密集型

  • 图像/视频处理
  • 3D 渲染
  • 加密/解密
  • 压缩/解压缩
  • 科学计算

需要性能的库

  • 游戏引擎
  • PDF 渲染器
  • 音频处理

8.2 不适合的场景

DOM 操作频繁:JavaScript 更方便 ❌ 简单逻辑:WASM 的引入成本不值得 ❌ 需要大量 JS API:互操作开销大


九、工具链

9.1 wasm-pack

主要功能

  • 编译 Rust 到 WASM
  • 生成 JavaScript 绑定
  • 生成 TypeScript 类型定义
  • 发布到 npm

9.2 wasm-bindgen

功能

  • Rust 和 JavaScript 互操作
  • 自动生成绑定代码
  • 类型转换

9.3 调试工具

Chrome DevTools

  • 支持 WASM 调试
  • 可以查看 WASM 模块
  • Source Map 支持

十、系列总结

10.1 我们学到了什么?

这个系列覆盖了 Rust 的核心内容:

第 1 篇:了解 Rust 的应用场景和真实案例 第 2 篇:掌握基础语法(变量、类型、函数、控制流、Struct/Enum) 第 3 篇:理解所有权、借用、生命周期(Rust 的灵魂) 第 4 篇:实战 CLI 工具(文件 I/O、错误处理、测试) 第 5 篇:Rust + WebAssembly(高性能 Web 应用)

10.2 下一步学习方向

深入 Rust

  • 异步编程(async/await、Tokio)
  • 宏(macro_rules!、过程宏)
  • 高级 trait(关联类型、泛型)

Web 开发

  • Actix-web / Axum(后端框架)
  • Yew / Leptos(前端框架)
  • Diesel / SQLx(数据库)

系统编程

  • 嵌入式开发
  • 操作系统开发
  • 网络编程

区块链

  • Solana 开发
  • Substrate 框架

10.3 推荐资源

书籍

视频

社区


十一、结语

Rust 的学习曲线确实陡峭,但一旦掌握,你会发现它的设计哲学非常优雅:

  • 安全:编译时保证内存安全
  • 快速:零成本抽象,性能媲美 C/C++
  • 并发:无畏并发,编译时检查数据竞争
  • 现代化:优秀的工具链和生态

无论是系统编程、Web 开发、WebAssembly 还是区块链,Rust 都是一个值得投资的语言。

希望这个系列对你有帮助。Happy coding! 🦀


参考资料


系列导航

系列完结!感谢阅读! 🎉