Rust 的 trait 系统是其类型系统的核心。理解这些标准 trait 不是背文档,而是要明白它们背后的设计意图和实际使用场景。

核心 Trait 分类

Rust 标准库的 trait 可以分为几个层次:

  1. 标记 Trait - 编译器特殊处理,无实际方法
  2. 派生 Trait - 几乎总是用 #[derive] 自动生成
  3. 操作 Trait - 运算符重载和类型转换
  4. 迭代 Trait - 迭代器生态系统
  5. I/O Trait - 输入输出抽象

标记 Trait(Marker Traits)

这些 trait 没有方法,纯粹用于向编译器传递类型信息。

Sized - 编译期已知大小

pub trait Sized {
    // 空 trait,编译器内置实现
}

关键点:

  • 绝大多数类型都是 Sized
  • ?Sized 表示”可能不是 Sized”,用于 trait object 和切片
  • 这是隐式 bound,不需要显式写出
// 这两个签名等价
fn foo<T>(x: T) {}
fn foo<T: Sized>(x: T) {}

// 允许动态大小类型
fn bar<T: ?Sized>(x: &T) {}  // 必须用引用

实际意义: 栈上分配需要知道大小。Sized 就是编译器的安全阀。

SendSync - 线程安全边界

pub unsafe auto trait Send {}
pub unsafe auto trait Sync {}
  • Send: 类型可以安全地跨线程转移所有权
  • Sync: 类型可以安全地跨线程共享引用(&TSend

关键性质:

// 这两个关系永远成立:
T: Sync  => &T: Send
T: Send + Sync => Arc<T>: Send + Sync

常见类型的线程安全属性:

类型SendSync原因
i32完全可复制,无内部状态
String堆数据独属于该值
Rc<T>引用计数非原子操作
Arc<T>✗ (除非 T: Sync)原子计数,但内部数据需单独保护
RefCell<T>运行时借用检查,非线程安全
Mutex<T>✓ (如果 T: Send)内部锁保护

手动实现 Send/Sync:

这是 unsafe 的,因为你在告诉编译器”相信我,这是安全的”。

use std::sync::Mutex;

struct MyType {
    data: *mut u8,  // 原始指针默认 !Send + !Sync
    mutex: Mutex<()>,
}

// 我们知道 data 只在 mutex 保护下访问
unsafe impl Send for MyType {}
unsafe impl Sync for MyType {}

警告: 错误实现这两个 trait 会导致 data race,这是未定义行为。

Unpin - 固定语义

pub auto trait Unpin {}

默认几乎所有类型都是 Unpin。只有自引用结构体需要 !Unpin

use std::pin::Pin;

// 自引用结构体
struct SelfReferential {
    data: String,
    ptr_to_data: *const String,  // 指向 data
}

impl !Unpin for SelfReferential {}  // 必须 Pin 住

// 使用 Pin 保证内存位置不变
let mut boxed = Box::pin(SelfReferential {
    data: String::from("hello"),
    ptr_to_data: std::ptr::null(),
});

// Pin<&mut T> 允许你安全地设置自引用
unsafe {
    let ptr = &boxed.data as *const String;
    Pin::get_unchecked_mut(boxed.as_mut()).ptr_to_data = ptr;
}

设计意图: 异步状态机需要自引用,Pin 就是为此而生。

派生 Trait(Derivable Traits)

这些 trait 应该总是#[derive] 生成,不要手写。

Clone - 显式复制

pub trait Clone: Sized {
    fn clone(&self) -> Self;
    fn clone_from(&mut self, source: &Self) { ... }
}

Copy 的区别:

  • Copy: 隐式、按位复制、原值仍可用
  • Clone: 显式调用 .clone()、可能昂贵、原值仍可用
#[derive(Clone)]
struct Config {
    name: String,
    values: Vec<i32>,
}

let c1 = Config { name: "test".into(), values: vec![1, 2, 3] };
let c2 = c1.clone();  // 堆数据也被复制

性能考虑: 对于包含堆分配的类型,clone() 可能很昂贵。考虑使用 Arc<str>Arc<[T]> 进行共享。

Copy - 隐式按位复制

pub trait Copy: Clone { }

实现条件(编译器检查):

  • 所有字段都是 Copy
  • 没有 Drop 实现
// 可以 Copy
#[derive(Copy, Clone)]
struct Point { x: f64, y: f64 }

// 不能 Copy,因为 String 是堆分配的
#[derive(Clone)]
struct Label { text: String }

选择 Copy 还是 Clone?

  • 类型很小(<= 2 个机器字)且没有堆分配 → Copy
  • 否则 → 只 Clone

DebugDisplay

pub trait Debug {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>;
}

pub trait Display {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>;
}
DebugDisplay
派生可以 #[derive(Debug)]必须手写
用途调试输出、日志用户可见输出
格式{:?}{:#?}{}
稳定性格式可能变化格式应稳定
#[derive(Debug)]
struct Point { x: f64, y: f64 }

impl std::fmt::Display for Point {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "({:.2}, {:.2})", self.x, self.y)
    }
}

let p = Point { x: 1.2345, y: 6.789 };
println!("Debug: {:?}", p);    // Debug: Point { x: 1.2345, y: 6.789 }
println!("Display: {}", p);    // Display: (1.23, 6.79)

实际建议:

  • 所有 public 类型都实现 Debug
  • 只有面向用户的类型才需要 Display
  • 错误类型应该同时实现两者

PartialEqEq

pub trait PartialEq<Rhs = Self> {
    fn eq(&self, other: &Rhs) -> bool;
    fn ne(&self, other: &Rhs) -> bool { !self.eq(other) }
}

pub trait Eq: PartialEq<Self> { }

区别:

  • PartialEq: 允许部分等价关系(不满足自反性)
  • Eq: 完整等价关系(满足自反性、对称性、传递性)
// f32/f64 只有 PartialEq,因为 NaN != NaN
let nan = f32::NAN;
assert!(nan != nan);  // true!

// 整数有 Eq
assert_eq!(5, 5);  // 总是成立

派生宏的局限性:

#[derive(PartialEq)]
struct Id(u64);

// 这会用字段的 PartialEq,对 Id 来说通常是对的
// 但如果你有浮点数字段,整个结构体只能是 PartialEq

PartialOrdOrd

pub trait PartialOrd<Rhs = Self>: PartialEq<Rhs> {
    fn partial_cmp(&self, other: &Rhs) -> Option<Ordering>;
    // ... lt, le, gt, ge
}

pub trait Ord: Eq + PartialOrd<Self> {
    fn cmp(&self, other: &Self) -> Ordering;
}

关键区别: PartialOrd 可能返回 None(不可比较),Ord 总是返回 Ordering

// 浮点数是 PartialOrd,因为 NaN 无法比较
assert!(f32::NAN.partial_cmp(&1.0).is_none());

// BTreeMap 需要 K: Ord,所以不能用 f32 做 key
// 解决方案:用 ordered_float  crate 或者自定义 wrapper

Hash - 用于哈希表

pub trait Hash {
    fn hash<H: Hasher>(&self, state: &mut H);
}

Eq 的契约:
如果 a == b,则 hash(a) == hash(b)。违反这个契约会导致 HashMap 行为异常。

#[derive(Hash, Eq, PartialEq)]
struct Person {
    id: u64,
    name: String,
}

// 错误:如果你手动实现 Eq 只比较 id,但 Hash 派生会包含 name
// 这会导致 id 相同但 name 不同的 Person 有不同的 hash 值
// 但却被认为是相等的!

操作 Trait

DerefDerefMut - 智能指针核心

pub trait Deref {
    type Target: ?Sized;
    fn deref(&self) -> &Self::Target;
}

pub trait DerefMut: Deref {
    fn deref_mut(&mut self) -> &mut Self::Target;
}

自动解引用(Deref coercion):

use std::ops::Deref;

struct MyBox<T>(T);

impl<T> Deref for MyBox<T> {
    type Target = T;
    fn deref(&self) -> &T { &self.0 }
}

let x = MyBox(String::from("hello"));
// 这里自动发生:&MyBox<String> -> &String -> &str
let s: &str = &x;

Deref coercion 规则:

  • &T&U,当 T: Deref<Target=U>
  • &mut T&mut U,当 T: DerefMut<Target=U>
  • &mut T&U,当 T: Deref<Target=U>(可变到不可变是安全的)

警告: 不要滥用 Deref 做隐式类型转换。它应该只用于智能指针。

Drop - 资源清理

pub trait Drop {
    fn drop(&mut self);
}

调用时机:

  • 值离开作用域
  • 值被赋值覆盖
  • 手动调用 mem::drop
struct DatabaseConnection {
    conn: *mut ffi::Connection,
}

impl Drop for DatabaseConnection {
    fn drop(&mut self) {
        unsafe { ffi::close_connection(self.conn); }
        println!("Connection closed");
    }
}

// 使用
{
    let conn = DatabaseConnection { conn: raw_ptr };
    // ... 使用 conn
} // 自动调用 drop

重要限制:

  • 不能手动调用(用 mem::drop 代替)
  • drop 接收 &mut self,不能转移所有权
  • 如果需要转移所有权,考虑使用 Option<T> + take()
impl Drop for MyType {
    fn drop(&mut self) {
        // 错误:不能转移 self.resource 的所有权
        // some_function(self.resource);
        
        // 正确做法
        if let Some(resource) = self.resource.take() {
            some_function(resource);
        }
    }
}

AsRefAsMut - 廉价引用转换

pub trait AsRef<T: ?Sized> {
    fn as_ref(&self) -> &T;
}

pub trait AsMut<T: ?Sized> {
    fn as_mut(&mut self) -> &mut T;
}

From/Into 的区别:

  • AsRef: 只产生引用,不消耗 self,无分配
  • From/Into: 可能消耗 self,可能有分配

使用场景 - 函数参数灵活性:

// 接受任何可以转换为 &str 的类型
fn print_info<S: AsRef<str>>(s: S) {
    println!("{}", s.as_ref());
}

print_info("literal");        // &str
print_info(String::from("s")); // String
print_info(std::path::Path::new("/tmp")); // Path -> &str

标准实现示例:

impl AsRef<str> for String { }
impl AsRef<str> for str { }
impl AsRef<Path> for str { }
impl AsRef<Path> for PathBuf { }
impl<T> AsRef<[T]> for Vec<T> { }

FromInto - 类型转换

pub trait From<T>: Sized {
    fn from(value: T) -> Self;
}

pub trait Into<T>: Sized {
    fn into(self) -> T;
}

关系: IntoFrom 的反向。标准库为 From 实现了 Into

impl<T, U> Into<U> for T where U: From<T> {
    fn into(self) -> U { U::from(self) }
}

实现原则:

  • 总是实现 FromInto 会自动获得
  • From 应该永不失败(如果可能失败,用 TryFrom
  • From 应该廉价,但允许分配
// 标准库中的典型实现
impl From<&str> for String {
    fn from(s: &str) -> String { s.to_owned() }
}

impl From<Vec<u8>> for String { }
impl From<u16> for u32 { }      // 无损扩展
impl From<i32> for f64 { }      // 精确转换

错误类型的 From 实现:

// 这让 ? 操作符可以自动转换错误类型
impl From<std::io::Error> for MyError {
    fn from(e: std::io::Error) -> Self {
        MyError::Io(e)
    }
}

fn read_file() -> Result<String, MyError> {
    let f = std::fs::File::open("file")?;  // io::Error 自动转为 MyError
    // ...
}

TryFromTryInto - 可能失败的转换

pub trait TryFrom<T>: Sized {
    type Error;
    fn try_from(value: T) -> Result<Self, Self::Error>;
}

使用场景:

  • 可能溢出的数值转换
  • 可能失败的字符串解析
  • 需要验证的输入转换
// u32 可能无法放入 u16
let big: u32 = 70000;
let small: u16 = big.try_into()?;  // Err(TryFromIntError)

// 字符串到数值
let num: i32 = "42".try_into()?;
let invalid: Result<i32, _> = "abc".try_into();  // Err(ParseIntError)

迭代 Trait

Iterator - 核心接口

pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
    
    // 数十个默认方法...
}

关键洞察: 只需要实现 next,其他方法都有默认实现。

struct Counter { count: u32 }

impl Iterator for Counter {
    type Item = u32;
    fn next(&mut self) -> Option<u32> {
        if self.count < 5 {
            self.count += 1;
            Some(self.count)
        } else {
            None
        }
    }
}

// 自动获得所有适配器
let sum: u32 = Counter { count: 0 }.sum();  // 15

IntoIterator - 可迭代转换

pub trait IntoIterator {
    type Item;
    type IntoIter: Iterator<Item = Self::Item>;
    fn into_iter(self) -> Self::IntoIter;
}

这是 for 循环的工作原理:

for x in collection { }
// 实际上展开为:
let mut iter = IntoIterator::into_iter(collection);
while let Some(x) = iter.next() { }

三种实现方式:

// 1. 消耗 self(Vec, HashMap)
impl IntoIterator for Vec<T> {
    type Item = T;
    type IntoIter = IntoIter<T>;
    fn into_iter(self) -> Self::IntoIter { ... }
}

// 2. 借用(&Vec<T> -> &T)
impl<'a, T> IntoIterator for &'a Vec<T> {
    type Item = &'a T;
    type IntoIter = Iter<'a, T>;
}

// 3. 可变借用(&mut Vec<T> -> &mut T)
impl<'a, T> IntoIterator for &'a mut Vec<T> {
    type Item = &'a mut T;
    type IntoIter = IterMut<'a, T>;
}

FromIterator - 从迭代器构建集合

pub trait FromIterator<A>: Sized {
    fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self;
}

这是 collect() 的工作原理:

let nums = vec![1, 2, 3];
let doubled: Vec<i32> = nums.iter().map(|x| x * 2).collect();

// collect 实际上是:
FromIterator::from_iter(iter)

用于自定义集合:

#[derive(Default)]
struct UniqueSet<T: Hash + Eq>(HashSet<T>);

impl<T: Hash + Eq> FromIterator<T> for UniqueSet<T> {
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
        UniqueSet(iter.into_iter().collect())
    }
}

// 现在可以直接 collect
let set: UniqueSet<i32> = [1, 2, 2, 3].into_iter().collect();

DoubleEndedIteratorExactSizeIterator

pub trait DoubleEndedIterator: Iterator {
    fn next_back(&mut self) -> Option<Self::Item>;
}

pub trait ExactSizeIterator: Iterator {
    fn len(&self) -> usize;
    fn is_empty(&self) -> bool { self.len() == 0 }
}

DoubleEndedIterator 允许从两端迭代:

let v = vec![1, 2, 3, 4, 5];
let mut iter = v.iter();

assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next_back(), Some(&5));
assert_eq!(iter.next(), Some(&2));

ExactSizeIterator 提供精确长度:

// 可以优化分配
let v: Vec<i32> = iter.collect();  // ExactSizeIterator 允许预分配

// 可以反向迭代而不消耗
let rev: Vec<_> = iter.rev().collect();

I/O Trait

Read - 字节输入

pub trait Read {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
    
    // 默认方法:
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> { ... }
    fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { ... }
    fn bytes(self) -> Bytes<Self> { ... }
    fn chain<R: Read>(self, next: R) -> Chain<Self, R> { ... }
}

实现者:

  • File, TcpStream, &[u8]
  • Stdin, Cursor<Vec<u8>>
  • GzDecoder<R>, BufReader<R>(适配器)

使用模式:

use std::io::{Read, Cursor};

// 从内存读取
let data = b"hello world";
let mut cursor = Cursor::new(data);
let mut buf = [0u8; 5];
cursor.read_exact(&mut buf)?;

// 流式处理
let mut file = File::open("large.bin")?;
let mut buf = vec![0u8; 8192];
loop {
    match file.read(&mut buf)? {
        0 => break,
        n => process(&buf[..n]),
    }
}

Write - 字节输出

pub trait Write {
    fn write(&mut self, buf: &[u8]) -> Result<usize>;
    fn flush(&mut self) -> Result<()>;
    
    fn write_all(&mut self, buf: &[u8]) -> Result<()> { ... }
    fn write_fmt(&mut self, fmt: Arguments<'_>) -> Result<()> { ... }
}

重要: write 可能只写入部分数据,所以 write_all 更常用。

use std::io::Write;

let mut buf = Vec::new();
buf.write_all(b"hello")?;
buf.write_all(b" world")?;

// 标准输出需要 flush
std::io::stdout().write_all(b"prompt: ")?;
std::io::stdout().flush()?;  // 确保立即显示

Seek - 随机访问

pub trait Seek {
    fn seek(&mut self, pos: SeekFrom) -> Result<u64>;
}

pub enum SeekFrom {
    Start(u64),
    End(i64),
    Current(i64),
}

使用场景:

use std::io::{Seek, SeekFrom, Read};

let mut file = File::open("data.bin")?;

// 读取文件末尾的 8 字节
file.seek(SeekFrom::End(-8))?;
let mut trailer = [0u8; 8];
file.read_exact(&mut trailer)?;

// 回到开头
file.seek(SeekFrom::Start(0))?;

BufRead - 缓冲读取

pub trait BufRead: Read {
    fn fill_buf(&mut self) -> Result<&[u8]>;
    fn consume(&mut self, amt: usize);
    
    // 默认方法:
    fn read_line(&mut self, buf: &mut String) -> Result<usize> { ... }
    fn lines(self) -> Lines<Self> { ... }
    fn split(self, byte: u8) -> Split<Self> { ... }
}

为什么需要 BufRead

// 低效:每次 read_line 都进行系统调用
let file = File::open("big.txt")?;
for line in file.lines() { }  // 编译错误:File 没有 lines()

// 高效:使用 BufReader 缓冲
let file = File::open("big.txt")?;
let reader = BufReader::new(file);
for line in reader.lines() { }  // 正确

错误处理 Trait

Error - 错误类型基础

pub trait Error: Debug + Display {
    fn source(&self) -> Option<&(dyn Error + 'static)> { None }
    // ... 其他默认方法
}

实现最佳实践:

use std::fmt;
use std::error::Error;
use std::io;

#[derive(Debug)]
enum AppError {
    Io(io::Error),
    Parse(String),
    Config { key: String, reason: String },
}

impl fmt::Display for AppError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            AppError::Io(e) => write!(f, "IO error: {}", e),
            AppError::Parse(s) => write!(f, "Parse error: {}", s),
            AppError::Config { key, reason } => {
                write!(f, "Config error for '{}': {}", key, reason)
            }
        }
    }
}

impl Error for AppError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self {
            AppError::Io(e) => Some(e),
            _ => None,
        }
    }
}

// 启用 ? 操作符
impl From<io::Error> for AppError {
    fn from(e: io::Error) -> Self { AppError::Io(e) }
}

实践建议

1. 为自己的类型实现哪些 trait?

必须实现:

  • Debug - 总是派生
  • Clone - 如果有意义
  • Default - 如果类型有”空”状态

根据用途实现:

  • PartialEq/Eq - 如果需要比较相等性
  • PartialOrd/Ord - 如果需要排序或作为 BTreeMap 的 key
  • Hash - 如果需要作为 HashMap 的 key
  • Display - 面向用户的输出
  • Serialize/Deserialize - 如果需要序列化(serde)

智能指针实现:

  • Deref/DerefMut
  • AsRef/AsMut
  • Borrow/BorrowMut(如果要做 key)

2. 避免常见陷阱

不要滥用 Deref

// 错误:Person 不是智能指针
struct Person { name: String }
impl Deref for Person {
    type Target = String;
    fn deref(&self) -> &String { &self.name }
}
// 这让 person.to_lowercase() 可以工作,但隐藏了复杂性

保持 From 转换廉价:

// 错误:From 不应该有副作用或网络操作
impl From<Config> for Database {
    fn from(cfg: Config) -> Self {
        Database::connect(&cfg.url).unwrap()  // 可能 panic!
    }
}

注意 Drop 中的 panic:

impl Drop for MyType {
    fn drop(&mut self) {
        // 不要在 drop 中 panic,这会导致 abort
        self.cleanup().expect("cleanup failed");  // 危险!
    }
}

3. 使用 thiserror 简化错误类型

use thiserror::Error;

#[derive(Error, Debug)]
enum AppError {
    #[error("IO error: {0}")]
    Io(#[from] io::Error),
    
    #[error("Invalid config: {key} - {reason}")]
    Config { key: String, reason: String },
    
    #[error("Parse error at line {line}: {msg}")]
    Parse { line: usize, msg: String },
}

总结

Rust 的 trait 系统是其类型系统的核心支柱。理解这些标准 trait 的关键在于:

  1. 标记 trait 告诉编译器类型的特殊属性
  2. 派生 trait 让编译器为你实现样板代码
  3. 操作 trait 提供一致的类型转换和运算符语义
  4. 迭代 trait 构建了强大的迭代器生态系统
  5. I/O trait 提供统一的抽象,支持组合

掌握这些 trait 不是记住每个方法的签名,而是理解它们背后的设计意图和组合方式。好的 Rust 代码往往是 trait 的自然组合,而不是复杂的类型体操。