Rust 桌面端与 WASM:Tauri、WebAssembly 与嵌入式开发

Rust 跨平台开发全景:Tauri 替代 Electron 的轻量级架构、WASM 编译(wasm-bindgen/wasm-pack)、WASI 运行时、嵌入式 Rust(no_std/embedded-hal),以及跨平台发布策略。

目录

  1. Tauri:Rust 驱动的桌面应用
  2. Tauri 2.0 与移动端支持
  3. WebAssembly 编译与交互
  4. WASI:WASM 系统接口
  5. 嵌入式 Rust
  6. 跨平台发布策略
  7. 总结与选型

1. Tauri:Rust 驱动的桌面应用

1.1 为什么替代 Electron

维度ElectronTauri
后端运行时Node.js + ChromiumRust + 系统 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 移除标准库,仅使用 corealloc

#![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. 总结与选型

场景技术方案生态成熟度
跨平台桌面 AppTauri 2.0⭐⭐⭐⭐⭐
浏览器内高性能计算WASM + wasm-bindgen⭐⭐⭐⭐⭐
插件/脚本系统WASI + Wasmtime⭐⭐⭐⭐
裸机 MCUEmbassy + embedded-hal⭐⭐⭐⭐
浏览器游戏引擎WASM + wgpu/WebGPU⭐⭐⭐⭐

Rust 在跨平台领域的优势在于一份代码,多处编译:Tauri 的前后端共享类型定义(通过 TypeScript 生成),WASM 的严格沙箱安全保障,嵌入式领域的零成本抽象——这些都是其他语言难以同时满足的。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「rust」更多文章