目录
- Tauri:Rust 驱动的桌面应用
- Tauri 2.0 与移动端支持
- WebAssembly 编译与交互
- WASI:WASM 系统接口
- 嵌入式 Rust
- 跨平台发布策略
- 总结与选型
- WASM 性能优化实战
- 嵌入式 Rust 进阶
- 选型决策矩阵
1. Tauri:Rust 驱动的桌面应用
1.1 为什么替代 Electron
| 维度 | Electron | Tauri |
|---|---|---|
| 后端运行时 | Node.js + Chromium | Rust + 系统 WebView |
| 打包体积 | ~150MB | ~3MB |
| 内存占用 | 高(完整 Chromium) | 低(共享系统 WebView) |
| 安全性 | 中等 | 高(Rust 内存安全 + 进程隔离) |
| 前端框架 | 任意 | 任意(React /Vue /Svelte/纯 HTML) |
1.2 项目结构
my-tauri-app/
├── src/ # 前端代码(任意框架)
├── src-tauri/ # Rust 后端
│ ├── src/main.rs
│ ├── Cargo.toml
│ └── tauri.conf.json
1.3 核心概念:Commands
// src-tauri/src/main.rs
use tauri::command;
#[command]
fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust.", name)
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
前端调用:
import { invoke } from '@tauri-apps/api/tauri';
const response = await invoke('greet', { name: 'World' });
console.log(response); // "Hello, World! You've been greeted from Rust."
1.4 状态管理
use tauri::{State, Manager};
use std::sync::Mutex;
struct AppState {
counter: Mutex<i32>,
}
#[command]
fn increment_counter(state: State<AppState>) -> i32 {
let mut counter = state.counter.lock().unwrap();
*counter += 1;
*counter
}
fn main() {
tauri::Builder::default()
.manage(AppState { counter: Mutex::new(0) })
.invoke_handler(tauri::generate_handler![increment_counter])
.run(tauri::generate_context!())
.unwrap();
}
1.5 文件系统访问
use tauri::api::path::document_dir;
use std::fs;
#[command]
fn save_file(filename: String, content: String) -> Result<(), String> {
let path = document_dir()
.ok_or("无法获取文档目录")?
.join(filename);
fs::write(path, content).map_err(|e| e.to_string())
}
1.6 窗口管理
use tauri::{WindowBuilder, WindowUrl};
fn create_settings_window(app: &tauri::AppHandle) {
WindowBuilder::new(
app,
"settings",
WindowUrl::App("settings.html".into())
)
.title("Settings")
.inner_size(600.0, 400.0)
.build()
.unwrap();
}
2. Tauri 2.0 与移动端支持
Tauri 2.0 新增了 iOS 和 Android 支持,使用系统 WebView(WKWebView / WebView):
cargo install tauri-cli@^2.0
cargo tauri android init
cargo tauri android dev
移动端特有的 API:
#[command]
async fn scan_qr_code() -> Result<String, String> {
// 调用移动端原生扫码能力
tauri_barcode_scanner::scan()
.await
.map_err(|e| e.to_string())
}
3. WebAssembly 编译与交互
3.1 为什么用 Rust 编译 WASM
- 无需 GC:WASM 目前没有 GC,Rust 的所有权模型完美契合
- 体积优化:wasm-opt 可进一步压缩,低至几 KB
- 性能接近原生:Wasmtime/Wasmer 运行时 JIT 编译
3.2 wasm-bindgen:Rust ↔ JS 桥梁
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
#[wasm_bindgen]
pub struct Point {
x: f64,
y: f64,
}
#[wasm_bindgen]
impl Point {
#[wasm_bindgen(constructor)]
pub fn new(x: f64, y: f64) -> Point {
Point { x, y }
}
pub fn distance(&self, other: &Point) -> f64 {
((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt()
}
}
JavaScript 调用:
import init, { add, Point } from './pkg/my_wasm.js';
await init();
console.log(add(1, 2)); // 3
const p1 = new Point(0, 0);
const p2 = new Point(3, 4);
console.log(p1.distance(p2)); // 5
3.3 wasm-pack 工作流
# 安装
cargo install wasm-pack
# 编译为浏览器适用的 WASM
wasm-pack build --target web
# 编译为 Node.js 模块
wasm-pack build --target nodejs
# 编译为 bundler(webpack/rollup/vite)
wasm-pack build --target bundler
3.4 web-sys 与浏览器 API
use wasm_bindgen::JsCast;
use web_sys::{HtmlCanvasElement, CanvasRenderingContext2d};
#[wasm_bindgen]
pub fn draw_circle(canvas_id: &str, x: f64, y: f64, r: f64) {
let document = web_sys::window().unwrap().document().unwrap();
let canvas = document.get_element_by_id(canvas_id)
.unwrap()
.dyn_into::<HtmlCanvasElement>()
.unwrap();
let context = canvas.get_context("2d")
.unwrap()
.unwrap()
.dyn_into::<CanvasRenderingContext2d>()
.unwrap();
context.begin_path();
context.arc(x, y, r, 0.0, std::f64::consts::PI * 2.0)
.unwrap();
context.stroke();
}
3.5 wee_alloc:缩小 WASM 体积
// 使用更小的分配器(适合 < 32KB 的场景)
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
4. WASI:WASM 系统接口
WASI(WebAssembly System Interface)让 WASM 在浏览器外运行,具有沙箱安全特性。
4.1 使用 Wasmtime 运行
use wasmtime::{Engine, Module, Store, Instance};
let engine = Engine::default();
let module = Module::from_file(&engine, "plugin.wasm")?;
let mut store = Store::new(&engine, ());
let instance = Instance::new(&mut store, &module, &[])?;
let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;
run.call(&mut store, ())?;
4.2 Capability-based 安全
use wasmtime_wasi::{WasiCtx, WasiCtxBuilder};
let wasi = WasiCtxBuilder::new()
.inherit_stdio()
.preopened_dir("./data", "/data")? // 仅开放特定目录
.build();
4.3 插件化架构
// Host(宿主应用)
pub trait Plugin {
fn process(&self, input: &str) -> String;
}
// 编译为 WASM 的插件
#[no_mangle]
pub extern "C" fn process(input_ptr: i32, input_len: i32) -> i32 {
// ... 使用 WASI 内存交互
}
5. 嵌入式 Rust
5.1 no_std 环境
no_std 移除标准库,仅使用 core 和 alloc:
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use panic_halt as _;
#[entry]
fn main() -> ! {
let peripherals = stm32f4:: Peripherals::take().unwrap();
let gpioa = peripherals.GPIOA;
// 配置引脚...
loop {
// 主循环
}
}
5.2 embedded-hal 抽象层
use embedded_hal::digital::OutputPin;
fn blink_led<T: OutputPin>(led: &mut T, delay_ms: u32) {
led.set_high().unwrap();
delay(delay_ms);
led.set_low().unwrap();
delay(delay_ms);
}
5.3 RTIC / Embassy 框架
| 框架 | 特点 |
|---|---|
| RTIC | 基于硬件任务调度,零成本抽象 |
| Embassy | 异步/await 嵌入式,类似 Tokio |
// Embassy 异步示例
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
let mut led = Output::new(p.PA5, Level::Low, Speed::Low);
loop {
led.set_high();
Timer::after(Duration::from_millis(300)).await;
led.set_low();
Timer::after(Duration::from_millis(300)).await;
}
}
5.4 Probe-rs 调试
# 无需 OpenOCD,纯 Rust 工具链
cargo install probe-rs --features cli
# 烧录
cargo embed --release
# 调试
cargo run --bin my_app
6. 跨平台发布策略
6.1 Cross 交叉编译
cargo install cross
# Linux → Windows
cross build --target x86_64-pc-windows-gnu
# Linux → macOS
cross build --target x86_64-apple-darwin
# Linux → ARM64
cross build --target aarch64-unknown-linux-gnu
6.2 cargo-dist
cargo install cargo-dist
# 配置 Cargo.toml
[workspace.metadata.dist]
targets = ["x86_64-unknown-linux-gnu", "x86_64-apple-darwin", "aarch64-apple-darwin"]
# 生成所有平台的发布包
cargo dist build
6.3 平台特定代码
#[cfg(target_os = "windows")]
fn platform_init() { /* Windows 特定初始化 */ }
#[cfg(target_os = "macos")]
fn platform_init() { /* macOS 特定初始化 */ }
#[cfg(target_os = "linux")]
fn platform_init() { /* Linux 特定初始化 */ }
7. 总结与选型
| 场景 | 技术方案 | 生态成熟度 |
|---|---|---|
| 跨平台桌面 App | Tauri 2.0 | ⭐⭐⭐⭐⭐ |
| 浏览器内高性能计算 | WASM + wasm-bindgen | ⭐⭐⭐⭐⭐ |
| 插件/脚本系统 | WASI + Wasmtime | ⭐⭐⭐⭐ |
| 裸机 MCU | Embassy + embedded-hal | ⭐⭐⭐⭐ |
| 浏览器游戏引擎 | WASM + wgpu/WebGPU | ⭐⭐⭐⭐ |
8. WASM 性能优化实战
8.1 编译优化选项
# Cargo.toml
[profile.release]
opt-level = 3 # 最高优化级别
lto = true # 链接时优化
codegen-units = 1 # 单 codegen unit,最大化优化
panic = "abort" # 移除 panic 处理开销
strip = true # 移除调试符号
8.2 wasm-opt 二进制优化
cargo install wasm-opt
# 优化 WASM 文件(通常可再压缩 20-40%)
wasm-opt -O3 -o optimized.wasm input.wasm
# 同时启用多种优化
wasm-opt -O3 --dce --remove-unused-names -o out.wasm in.wasm
8.3 与 JavaScript 的性能对比
| 场景 | JavaScript | WASM (Rust) | 加速比 |
|---|---|---|---|
| 图像灰度化(10MB) | 45ms | 3ms | 15x |
| SHA-256 哈希(1MB) | 12ms | 0.8ms | 15x |
| JSON 解析(复杂对象) | 8ms | 5ms | 1.6x |
| DOM 操作 | 原生最快 | 需通过 JS 桥接 | JS 更优 |
结论:WASM 适合计算密集型任务,DOM/IO 操作仍推荐原生 JavaScript。
8.4 SIMD 加速
Rust 1.54+ 支持 WASM SIMD(128-bit):
#[cfg(target_arch = "wasm32")]
use std::arch::wasm32::*;
pub fn simd_add(a: &[f32], b: &[f32], result: &mut [f32]) {
unsafe {
for i in (0..a.len()).step_by(4) {
let va = v128_load(a.as_ptr().add(i) as *const v128);
let vb = v128_load(b.as_ptr().add(i) as *const v128);
let vr = f32x4_add(va, vb);
v128_store(result.as_mut_ptr().add(i) as *mut v128, vr);
}
}
}
编译时启用 SIMD:
RUSTFLAGS="-C target-feature=+simd128" wasm-pack build
9. 嵌入式 Rust 进阶
9.1 RTIC(实时中断驱动并发)
#[rtic::app(device = stm32f4xx_hal::pac, peripherals = true)]
mod app {
#[shared]
struct Shared {
serial: Serial<USART1>,
}
#[local]
struct Local {}
#[init]
fn init(cx: init::Context) -> (Shared, Local) {
let serial = setup_usart1(cx.device.USART1);
(Shared { serial }, Local {})
}
#[task(binds = TIM2, shared = [serial])]
fn tim2_irq(mut cx: tim2_irq::Context) {
cx.shared.serial.lock(|serial| {
writeln!(serial, "定时器中断触发").unwrap();
});
}
}
RTIC 利用 Cortex-M 的 NVIC 硬件调度,零运行时开销,比传统 RTOS 更轻量。
9.2 Embassy 异步框架
#[embassy_executor::task]
async fn sensor_reader(mut i2c: I2c<'static>) {
let mut sensor = Bmp280::new(&mut i2c).await.unwrap();
let mut ticker = Ticker::every(Duration::from_secs(1));
loop {
let measurement = sensor.measure(&mut i2c).await.unwrap();
info!("温度: {}°C, 气压: {}hPa", measurement.temperature, measurement.pressure);
ticker.next().await;
}
}
#[embassy_executor::main]
async fn main(spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
let i2c = I2c::new(p.I2C1, p.PB6, p.PB7, Hertz(100_000));
spawner.spawn(sensor_reader(i2c)).unwrap();
}
9.3 defmt 零开销日志
use defmt::*;
#[entry]
fn main() -> ! {
info!("系统启动,版本: {}", VERSION); // 编译时格式化
debug!("配置: {:?}", config); // 调试模式下输出
let sensor_val = read_sensor();
assert!(sensor_val > 0, "传感器读取失败"); // 断言保留文件位置
loop {}
}
defmt 通过延迟格式化(deferred formatting)将日志开销降至最低,相比 println! 节省 90%+ 的固件空间。
10. 选型决策矩阵
| 场景 | 首选方案 | 次选方案 | 关键考量 |
|---|---|---|---|
| 跨平台桌面 App(< 50MB) | Tauri 2.0 | Flutter Desktop | 包体积、前端生态 |
| 浏览器内重计算(图像/音频) | WASM + Rust | WASM + C/C++ | 安全、工具链 |
| 插件/脚本沙箱 | WASI + Wasmtime | Lua/JS 引擎 | 安全隔离、标准化 |
| 裸机 MCU(< 512KB Flash) | Embassy + embedded-hal | RTIC | 异步支持、生态 |
| 浏览器端游戏引擎 | WASM + wgpu | Godot Web Export | 性能、渲染能力 |
| CLI 工具跨平台发布 | cargo-dist | Go Releaser | Rust 工具链原生支持 |
| 微服务 WASM 运行时 | Wasmtime/Spin | WasmEdge | 云原生集成 |
Rust 在跨平台领域的优势在于一份代码,多处编译:Tauri 的前后端共享类型定义(通过 TypeScript 生成),WASM 的严格沙箱安全保障,嵌入式领域的零成本抽象——这些都是其他语言难以同时满足的。随着 Rust 工具链的成熟(cargo-dist 一键发布、wasm-pack 零配置构建),跨平台开发的门槛正在快速降低,Rust 有望成为全栈跨平台开发的首选语言之一。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。