Rust Trait 与泛型的工程化使用
wxk1991 Lv5

Rust Trait 与泛型的工程化使用

Trait 是 Rust 抽象能力的核心。它有点像其他语言里的接口,但和泛型、所有权、静态分发结合后,能写出性能很高又边界清晰的代码。


一、Trait 定义行为

Trait 描述一组能力:

1
2
3
4
trait Storage {
fn get(&self, key: &str) -> Option<String>;
fn set(&mut self, key: &str, value: String);
}

任何类型只要实现这些方法,就可以被当作 Storage 使用。


二、为结构体实现 Trait

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use std::collections::HashMap;

struct MemoryStorage {
data: HashMap<String, String>,
}

impl Storage for MemoryStorage {
fn get(&self, key: &str) -> Option<String> {
self.data.get(key).cloned()
}

fn set(&mut self, key: &str, value: String) {
self.data.insert(key.to_string(), value);
}
}

这样业务逻辑就不必绑定具体实现。


三、泛型适合编译期确定的抽象

1
2
3
fn save_token<S: Storage>(storage: &mut S, token: String) {
storage.set("token", token);
}

编译器会为具体类型生成对应代码,运行时没有虚表开销,性能很好。


四、dyn Trait 适合运行时切换

如果需要在运行时决定具体实现,可以使用 trait object:

1
2
3
fn read_token(storage: &dyn Storage) -> Option<String> {
storage.get("token")
}

这会有一点动态分发成本,但换来了更灵活的组合能力。


五、工程建议

不要为了抽象而抽象。Trait 最适合放在这些位置:

  • 外部依赖边界,比如数据库、缓存、消息队列
  • 多种实现会被真实使用的模块
  • 单元测试需要替换真实服务的地方

Rust 的 Trait 很强,但过早铺满 Trait 会让代码变难读。先写具体实现,等重复和边界真实出现后再抽象,通常更稳。