Skip to content

Rust 实战教程(三):项目代码详解

本文结合实际项目代码,深入讲解 Rust 的高级特性、FFI、宏编程等实战应用。

1. 项目代码中的核心概念解析

1.1 N-API 集成

你的代码使用 #[napi] 宏将 Rust 函数导出给 Node.js:

rust
use napi_derive::napi;

#[napi]
pub fn init(surface_id: String, width: u32, height: u32, _opts: InitOptions) -> Result<()> {
    // 函数实现
}

要点解析:

  • #[napi] 是过程宏,用于自动生成 FFI 绑定代码
  • Result<()> 表示可能失败的函数(无返回值)
  • Rust 的类型自动映射到 JavaScript 类型

1.2 结构体与 N-API

rust
#[napi(object)]
pub struct InitOptions {
    pub age: u32,
    pub life: u32,
}

关键点:

  • #[napi(object)] 将结构体映射为 JavaScript 对象
  • pub 字段会被导出到 JavaScript
  • 类型映射:u32 → JavaScript number

1.3 静态可变变量

rust
static mut R: Option<GLRenderer> = None;

问题与解决方案:

  • static mut 是全局可变状态,不安全
  • 必须用 unsafe 块访问
  • 更好的做法是使用 MutexArc<Mutex<T>>
rust
// 更好的实现
use std::sync::Mutex;

static R: Mutex<Option<GLRenderer>> = Mutex::new(None);

// 使用
let mut r = R.lock().unwrap();
if let Some(renderer) = r.as_mut() {
    // ...
}

1.4 错误处理模式

rust
pub fn new(surface_id: String, width: u32, height: u32) -> napi::Result<Self> {
    unsafe {
        let display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
        if display == EGL_NO_DISPLAY {
            return Err(napi::Error::from_reason("eglGetDisplay failed"));
        }
        // ...
        Ok(Self { /* ... */ })
    }
}

模式分析:

  • 使用 Result<T, E> 进行错误传播
  • Err() 返回错误,Ok() 返回成功值
  • 错误信息会传递到 JavaScript

2. 外部函数接口(FFI)

2.1 C 函数声明

rust
extern "C" {
    fn eglGetDisplay(native_display: *mut c_void) -> EGLDisplay;
    fn eglInitialize(dpy: EGLDisplay, major: *mut i32, minor: *mut i32) -> i32;
}

关键概念:

  1. extern "C" - 指定 C 调用约定
  2. 裸指针 - *mut c_void 是可变裸指针
  3. 类型别名 - type EGLDisplay = *mut c_void;

2.2 指针类型

rust
// 不同类型指针示例
let raw_ptr: *const i32 = &x as *const i32;     // 不可变裸指针
let mut_ptr: *mut i32 = &mut x as *mut i32;      // 可变裸指针
let ref_: &i32 = &x;                             // 引用(安全)

// 解引用(需要 unsafe)
unsafe {
    let value = *raw_ptr;
}

2.3 原始类型转换

rust
#[repr(C)]  // C 兼容的内存布局
struct Point {
    x: f32,
    y: f32,
}

let bytes = std::mem::size_of::<Point>();
let byte_slice: &[u8] = unsafe {
    std::slice::from_raw_parts(
        point as *const Point as *const u8,
        bytes
    )
};

3. 内存管理和 RAII

3.1 Drop Trait

你的代码实现了 Drop 来清理资源:

rust
impl Drop for GLRenderer {
    fn drop(&mut self) {
        unsafe {
            eglMakeCurrent(self.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
            // 删除所有 GL 资源
            if let Some(v) = self.vao_future {
                self.gl.delete_vertex_array(v);
            }
            // ...
        }
    }
}

RAII(Resource Acquisition Is Initialization)模式:

  • 资源在对象创建时获取
  • 在对象销毁时自动释放
  • 无需手动管理内存

3.2 实例化渲染

rust
// 使用 Vec 动态分配
let mut future = Vec::new();

for _yi in (current_index + 1)..life_years {
    if let Some((c, r)) = free.first().cloned() {
        future.push(Instance {
            pos: to_world(c, r),
            color: color_future,
        });
        free.remove(0);
    }
}

内存管理要点:

  • Vec 会自动增长和缩小
  • 超出作用域时自动释放
  • 无需手动 freedelete

4. 迭代器和函数式编程

4.1 map 和 collect

rust
let raw: Vec<RawInst> = instances.iter()
    .map(|it| RawInst { pos: it.pos, color: it.color })
    .collect();

步骤分解:

  1. instances.iter() - 创建迭代器
  2. .map(|it| ...) - 转换每个元素
  3. .collect() - 收集到 Vec

等价的循环写法:

rust
let mut raw = Vec::new();
for it in instances.iter() {
    raw.push(RawInst { pos: it.pos, color: it.color });
}

4.2 更多迭代器方法

rust
// filter - 过滤
let evens: Vec<i32> = numbers.iter()
    .filter(|&&x| x % 2 == 0)
    .collect();

// fold - 累积
let sum = numbers.iter()
    .fold(0, |acc, x| acc + x);

// enumerate - 带索引
for (i, item) in vec.iter().enumerate() {
    println!("{}: {}", i, item);
}

4.3 链式调用

rust
let result = data.iter()
    .filter(|x| x > &&0)
    .map(|x| x * 2)
    .take(10)
    .collect::<Vec<_>>();

5. 闭包(Closures)

5.1 闭包示例

rust
let multiplier = 2;
let closure = |x| x * multiplier;

// 使用
let result = closure(5); // 10

5.2 闭包捕获

rust
// 移动捕获
let data = vec![1, 2, 3];
let closure = move || {
    println!("{:?}", data);
    // data 的所有权移入闭包
};

// 可变捕获
let mut counter = 0;
let mut closure = || {
    counter += 1;
};

5.3 函数指针与闭包

rust
fn apply<F>(f: F) -> i32
where
    F: FnOnce() -> i32,
{
    f()
}

let closure = || 42;
let result = apply(closure);

6. 宏编程

6.1 派生宏(Derive)

rust
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct Instance {
    pub pos: [f32; 3],
    pub color: [f32; 4],
}

可派生特征:

  • Clone - 克隆对象
  • Copy - 拷贝语义(栈上复制)
  • Debug - 格式化输出
  • Serialize/Deserialize - 序列化

6.2 属性宏

rust
#[napi]
pub fn init() -> Result<()> { }

// 展开后生成:
extern "C" fn init_binding(env: napi::Env, ...) -> napi::Value {
    // 自动生成的绑定代码
}

6.3 声明宏

rust
macro_rules! my_vec {
    ($($x:expr),*) => {
        {
            let mut temp_vec = Vec::new();
            $(
                temp_vec.push($x);
            )*
            temp_vec
        }
    };
}

// 使用
let v = my_vec!(1, 2, 3);

7. 并发模式

7.1 懒加载(Lazy Static)

rust
use once_cell::sync::Lazy;

static RUNNING: Lazy<Mutex<bool>> = Lazy::new(|| Mutex::new(false));

// 使用
*RUNNING.lock().unwrap() = true;

为什么需要 Lazy

  • 静态变量初始化必须是常量表达式
  • Lazy::new() 允许运行时初始化
  • 只初始化一次(线程安全)

7.2 互斥锁

rust
use parking_lot::Mutex;

let data = Arc::new(Mutex::new(0));

let data_clone = Arc::clone(&data);
thread::spawn(move || {
    let mut num = data_clone.lock();  // parking_lot 更快
    *num += 1;
});

parking_lot vs std::sync::Mutex:

  • parking_lot 实现更高效
  • std::sync::Mutex 可能发生系统调用
  • 小项目两者性能差异不大

8. 错误处理最佳实践

8.1 ? 运算符链

rust
fn complex_operation() -> Result<()> {
    let display = eglGetDisplay(EGL_DEFAULT_DISPLAY)?;
    eglInitialize(display, ...)?;
    // ...
    Ok(())
}

8.2 错误转换

rust
use std::fmt;

#[derive(Debug)]
enum MyError {
    IO(std::io::Error),
    Parse(String),
}

impl fmt::Display for MyError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            MyError::IO(e) => write!(f, "IO error: {}", e),
            MyError::Parse(s) => write!(f, "Parse error: {}", s),
        }
    }
}

impl std::error::Error for MyError {}

9. 类型系统技巧

9.1 类型别名

rust
type EGLDisplay = *mut c_void;
type UserId = u64;
type TimeStamp = i64;

9.2 新类型模式

rust
struct UserId(u64);

impl UserId {
    fn new(id: u64) -> Self {
        UserId(id)
    }
}

9.3 泛型参数

rust
struct Container<T> {
    items: Vec<T>,
}

impl<T> Container<T> {
    fn new() -> Self {
        Container { items: Vec::new() }
    }
}

10. 性能优化技巧

10.1 使用 bytemuck 零拷贝

rust
use bytemuck::{Pod, Zeroable};

#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
struct Vertex {
    x: f32,
    y: f32,
}

// 直接转换字节(无拷贝)
let bytes = bytemuck::cast_slice(&vertices);

10.2 预分配容量

rust
let mut vec = Vec::with_capacity(1000);  // 预留空间

10.3 使用 Cow 避免克隆

rust
use std::borrow::Cow;

fn process_data(data: Cow<str>) {
    match data {
        Cow::Borrowed(s) => println!("借用: {}", s),
        Cow::Owned(s) => println!("拥有: {}", s),
    }
}

11. 测试

11.1 单元测试

rust
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_add() {
        assert_eq!(add(2, 3), 5);
    }

    #[test]
    #[should_panic]
    fn test_divide_by_zero() {
        divide(1.0, 0.0).unwrap();
    }
}

11.2 集成测试

rust
// tests/integration_test.rs
use my_crate;

#[test]
fn test_integration() {
    let result = my_crate::function();
    assert!(result.is_ok());
}

项目代码实践建议

1. 改进错误处理

rust
// 当前代码
if display == EGL_NO_DISPLAY {
    return Err(napi::Error::from_reason("eglGetDisplay failed"));
}

// 更好的做法
display.ok_or_else(|| napi::Error::from_reason("eglGetDisplay failed"))?;

2. 使用 Option 模式

rust
// 改进前
pub fn start() -> Result<()> {
    unsafe {
        if let Some(r) = R.as_mut() {
            r.start_render_loop();
        }
    }
    Ok(())
}

// 改进后
pub fn start() -> Result<()> {
    unsafe {
        R.as_mut()
            .ok_or_else(|| napi::Error::from_reason("Not initialized"))?
            .start_render_loop();
    }
    Ok(())
}

3. 分离关注点

rust
// 将初始化逻辑分离
impl GLRenderer {
    fn create_context(&self) -> napi::Result<EGLContext> {
        // 上下文创建逻辑
    }

    fn create_surface(&self) -> napi::Result<EGLSurface> {
        // 表面创建逻辑
    }
}

总结

核心概念回顾

  1. 所有权系统 - 自动内存管理
  2. 错误处理 - ResultOption
  3. 特征 - 多态和代码复用
  4. 泛型 - 类型安全的抽象
  5. 并发 - MutexArc、线程
  6. - 元编程能力
  7. FFI - C 互操作

学习路径

  1. 掌握基础语法和所有权
  2. 理解错误处理和并发
  3. 学习 FFI 和宏编程
  4. 阅读优秀开源项目
  5. 编写实际项目

推荐资源


上一篇:Rust 入门进阶教程(二):模块、特征和并发
下一篇:回到基础教程

本站总访问