Rust - 常见 Trait 详解

文章目录[x]
  1. 1:Copy
  2. 1.1:概述
  3. 1.2:特点
  4. 1.3:实现条件
  5. 1.4:示例
  6. 1.5:适用类型
  7. 2:Clone
  8. 2.1:概述
  9. 2.2:特点
  10. 2.3:定义
  11. 2.4:示例
  12. 2.5:Copy vs Clone 对比
  13. 3:Send
  14. 3.1:概述
  15. 3.2:特点
  16. 3.3:定义
  17. 3.4:示例
  18. 3.5:常见的非 Send 类型
  19. 3.6:常见的 Send 类型
  20. 4:Sync
  21. 4.1:概述
  22. 4.2:特点
  23. 4.3:定义
  24. 4.4:示例
  25. 4.5:Send 和 Sync 的关系
  26. 4.6:常见的非 Sync 类型
  27. 5:Drop
  28. 5.1:概述
  29. 5.2:特点
  30. 5.3:定义
  31. 5.4:示例
  32. 5.5:注意事项
  33. 6:Default
  34. 6.1:概述
  35. 6.2:定义
  36. 6.3:示例
  37. 7:Debug
  38. 7.1:概述
  39. 7.2:定义
  40. 7.3:示例
  41. 8:Display
  42. 8.1:概述
  43. 8.2:定义
  44. 8.3:示例
  45. 9:PartialEq 和 Eq
  46. 9.1:PartialEq
  47. 9.2:Eq
  48. 9.3:示例
  49. 10:PartialOrd 和 Ord
  50. 10.1:PartialOrd
  51. 10.2:Ord
  52. 10.3:示例
  53. 11:From 和 Into
  54. 11.1:From
  55. 11.2:Into
  56. 11.3:示例
  57. 12:AsRef 和 AsMut
  58. 12.1:AsRef
  59. 12.2:AsMut
  60. 12.3:示例
  61. 13:Deref 和 DerefMut
  62. 13.1:Deref
  63. 13.2:DerefMut
  64. 13.3:示例
  65. 13.4:Deref 强制转换规则
  66. 14:Iterator
  67. 14.1:概述
  68. 14.2:定义
  69. 14.3:示例
  70. 14.4:三种迭代器方法
  71. 15:总结对比表
  72. 16:实践建议
  73. 17:参考资源

本文将介绍Rust开发中常见的trait。

Copy

概述

Copy 是一个标记 trait(marker trait),表示类型的值可以通过简单的位拷贝(bitwise copy)来复制。

特点

  • 自动复制:赋值或传参时自动复制值,而不是移动所有权
  • 栈上分配:只能用于完全存储在栈上的类型
  • 隐式行为:复制是隐式发生的,无需显式调用
  • 必须实现 CloneCopyClone 的子 trait

实现条件

  • 类型的所有字段都必须实现 Copy
  • 不能有自定义的 Drop 实现
  • 通常是简单的标量类型或由这些类型组成的结构体

示例

// 基本类型都实现了 Copy
let x = 5;
let y = x; // x 被复制,而非移动
println!("x = {}, y = {}", x, y); // x 仍然有效

// 自定义类型实现 Copy
#[derive(Copy, Clone)]
struct Point {
    x: i32,
    y: i32,
}

let p1 = Point { x: 1, y: 2 };
let p2 = p1; // p1 被复制
println!("p1: ({}, {})", p1.x, p1.y); // p1 仍然有效

// 不能实现 Copy 的例子(包含堆分配)
struct NotCopy {
    data: String, // String 不是 Copy,所以整个结构体不能是 Copy
}

适用类型

  • 所有整数类型(i8, u32, etc.)
  • 浮点类型(f32, f64)
  • 布尔类型(bool)
  • 字符类型(char)
  • 元组(如果所有元素都是 Copy)
  • 数组(如果元素类型是 Copy)

Clone

概述

Clone trait 提供显式的深拷贝功能,允许复制堆上的数据。

特点

  • 显式调用:需要明确调用 .clone() 方法
  • 深拷贝:可以复制堆上的数据
  • 可能昂贵:克隆操作可能涉及大量内存分配

定义

pub trait Clone {
    fn clone(&self) -> Self;

    fn clone_from(&mut self, source: &Self) {
        *self = source.clone()
    }
}

示例

// String 实现了 Clone
let s1 = String::from("hello");
let s2 = s1.clone(); // 显式克隆
println!("s1 = {}, s2 = {}", s1, s2);

// 自定义类型
#[derive(Clone)]
struct Person {
    name: String,
    age: u32,
}

let person1 = Person {
    name: String::from("Alice"),
    age: 30,
};
let person2 = person1.clone();

// 手动实现 Clone
struct CustomData {
    data: Vec<i32>,
}

impl Clone for CustomData {
    fn clone(&self) -> Self {
        println!("克隆操作被执行");
        CustomData {
            data: self.data.clone(),
        }
    }
}

Copy vs Clone 对比

特性 Copy Clone
调用方式 隐式 显式 .clone()
适用范围 仅栈上数据 栈和堆数据
性能 快速(位拷贝) 可能较慢
实现要求 必须实现 Clone 独立 trait

Send

概述

Send 是一个标记 trait,表示类型的所有权可以安全地在线程间转移。

特点

  • 自动实现:大多数类型自动实现
  • 编译器保证:在编译时确保线程安全
  • 所有权转移:允许将值的所有权从一个线程发送到另一个线程

定义

pub unsafe auto trait Send { }

示例

use std::thread;

// 实现了 Send 的类型
let data = vec![1, 2, 3, 4];
let handle = thread::spawn(move || {
    println!("数据: {:?}", data); // data 的所有权转移到新线程
});
handle.join().unwrap();

// 不实现 Send 的例子:Rc
use std::rc::Rc;

let rc = Rc::new(5);
// 下面的代码会编译错误,因为 Rc 不是 Send
// let handle = thread::spawn(move || {
//     println!("{}", rc);
// });

常见的非 Send 类型

  • Rc<T>:引用计数指针(非线程安全)
  • *const T*mut T:裸指针
  • std::rc::Weak<T>

常见的 Send 类型

  • 基本类型(i32, f64, bool, etc.)
  • String, Vec<T>(如果 T 是 Send)
  • Arc<T>(如果 T 是 Send + Sync)
  • Mutex<T>, RwLock<T>

Sync

概述

Sync 是一个标记 trait,表示类型可以安全地在多个线程间共享引用。

特点

  • 引用共享&T 可以在线程间传递
  • 关系TSync 当且仅当 &TSend
  • 自动实现:由编译器自动推导

定义

pub unsafe auto trait Sync { }

示例

use std::sync::{Arc, Mutex};
use std::thread;

// Arc<Mutex<T>> 允许多线程共享
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];

for _ in 0..10 {
    let counter_clone = Arc::clone(&counter);
    let handle = thread::spawn(move || {
        let mut num = counter_clone.lock().unwrap();
        *num += 1;
    });
    handles.push(handle);
}

for handle in handles {
    handle.join().unwrap();
}

println!("结果: {}", *counter.lock().unwrap());

Send 和 Sync 的关系

// 如果 T 实现了 Sync,那么 &T 实现了 Send
// 如果 T 实现了 Send,那么 T 可以被移动到其他线程

// 组合规则:
// - T: Send + Sync => 可以在线程间安全移动和共享
// - T: Send + !Sync => 可以移动,但不能共享(如 Cell)
// - T: !Send + Sync => 不常见
// - T: !Send + !Sync => 只能在单线程使用(如 Rc)

常见的非 Sync 类型

  • Cell<T>RefCell<T>:内部可变性,非线程安全
  • Rc<T>:引用计数,非线程安全
  • *const T*mut T:裸指针

Drop

概述

Drop trait 允许在值离开作用域时执行清理代码。

特点

  • 自动调用:变量离开作用域时自动调用
  • 资源清理:用于释放资源(文件、网络连接、锁等)
  • 不能手动调用:不能直接调用 drop() 方法,使用 std::mem::drop() 代替

定义

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

示例

struct FileHandler {
    filename: String,
}

impl Drop for FileHandler {
    fn drop(&mut self) {
        println!("关闭文件: {}", self.filename);
    }
}

fn main() {
    let _file = FileHandler {
        filename: String::from("data.txt"),
    };
    println!("文件处理器创建完成");
    // _file 在这里离开作用域,自动调用 drop
}

// 提前释放
let x = String::from("hello");
std::mem::drop(x); // 显式释放
// println!("{}", x); // 编译错误:x 已被释放

注意事项

  • 实现了 Drop 的类型不能实现 Copy
  • Drop 的执行顺序:变量按声明的相反顺序被 drop
  • 析构顺序:先 drop 父结构体,再 drop 字段

Default

概述

Default trait 提供类型的默认值。

定义

pub trait Default {
    fn default() -> Self;
}

示例

// 使用默认值
let v: Vec<i32> = Default::default();
let s: String = Default::default();

// 自定义默认值
#[derive(Default)]
struct Config {
    debug: bool,        // false
    max_connections: u32, // 0
}

// 手动实现
struct CustomConfig {
    timeout: u64,
    retries: u32,
}

impl Default for CustomConfig {
    fn default() -> Self {
        CustomConfig {
            timeout: 30,
            retries: 3,
        }
    }
}

// 结构体更新语法
let config = Config {
    debug: true,
    ..Default::default()
};

Debug

概述

Debug trait 用于格式化输出,主要用于调试。

定义

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

示例

#[derive(Debug)]
struct User {
    username: String,
    age: u32,
}

let user = User {
    username: String::from("alice"),
    age: 30,
};

println!("{:?}", user);  // User { username: "alice", age: 30 }
println!("{:#?}", user); // 美化输出

// 手动实现
struct Point {
    x: i32,
    y: i32,
}

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

Display

概述

Display trait 用于面向用户的格式化输出。

定义

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

示例

struct Point {
    x: i32,
    y: i32,
}

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

let p = Point { x: 3, y: 4 };
println!("{}", p); // (3, 4)

// Display vs Debug
// {:?} 或 {:#?} => Debug
// {}            => Display

PartialEq 和 Eq

PartialEq

用于部分等价关系,支持 ==!= 运算符。

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

Eq

用于完全等价关系,是 PartialEq 的子 trait。

pub trait Eq: PartialEq<Self> { }

示例

#[derive(PartialEq, Eq)]
struct Point {
    x: i32,
    y: i32,
}

let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: 1, y: 2 };
assert_eq!(p1, p2);

// 浮点数只实现 PartialEq,不实现 Eq
// 因为 NaN != NaN
let x = f64::NAN;
assert!(x != x);

// 自定义实现
struct CaseInsensitiveString(String);

impl PartialEq for CaseInsensitiveString {
    fn eq(&self, other: &Self) -> bool {
        self.0.to_lowercase() == other.0.to_lowercase()
    }
}

PartialOrd 和 Ord

PartialOrd

用于部分排序,支持 <, <=, >, >= 运算符。

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

Ord

用于全序关系。

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

示例

use std::cmp::Ordering;

#[derive(Eq, PartialEq, PartialOrd, Ord)]
struct Version {
    major: u32,
    minor: u32,
    patch: u32,
}

let v1 = Version { major: 1, minor: 2, patch: 3 };
let v2 = Version { major: 1, minor: 3, patch: 0 };
assert!(v1 < v2);

// 手动实现
struct Person {
    name: String,
    age: u32,
}

impl PartialOrd for Person {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.age.partial_cmp(&other.age)
    }
}

impl Ord for Person {
    fn cmp(&self, other: &Self) -> Ordering {
        self.age.cmp(&other.age)
    }
}

From 和 Into

From

用于值到值的转换。

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

Into

From 的倒置,自动实现。

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

示例

// From 示例
let s = String::from("hello");
let num = i32::from(5u8);

// 自定义 From
struct Kilometers(i32);

impl From<Miles> for Kilometers {
    fn from(miles: Miles) -> Self {
        Kilometers((miles.0 as f64 * 1.60934) as i32)
    }
}

struct Miles(i32);

let miles = Miles(10);
let km: Kilometers = Kilometers::from(miles);
// 或使用 Into
let miles = Miles(10);
let km: Kilometers = miles.into();

// 在函数参数中使用
fn print_string(s: impl Into<String>) {
    let string = s.into();
    println!("{}", string);
}

print_string("hello");        // &str -> String
print_string(String::from("world")); // String -> String

AsRef 和 AsMut

AsRef

用于廉价的引用到引用转换。

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

AsMut

可变引用版本。

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

示例

// AsRef 用于泛型参数
fn print_str(s: impl AsRef<str>) {
    println!("{}", s.as_ref());
}

print_str("hello");
print_str(String::from("world"));

// String 和 Vec 的 AsRef 实现
let s = String::from("hello");
let slice: &str = s.as_ref();

let v = vec![1, 2, 3];
let slice: &[i32] = v.as_ref();

// Path 示例
use std::path::Path;

fn process_path(p: impl AsRef<Path>) {
    let path = p.as_ref();
    println!("{:?}", path);
}

process_path("file.txt");
process_path(Path::new("/usr/bin"));

Deref 和 DerefMut

Deref

允许通过 * 运算符解引用。

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

DerefMut

可变版本。

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

示例

use std::ops::Deref;

struct MyBox<T>(T);

impl<T> MyBox<T> {
    fn new(x: T) -> MyBox<T> {
        MyBox(x)
    }
}

impl<T> Deref for MyBox<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.0
    }
}

let x = MyBox::new(5);
assert_eq!(5, *x); // 解引用

// Deref 强制转换
fn hello(name: &str) {
    println!("Hello, {}!", name);
}

let m = MyBox::new(String::from("Rust"));
hello(&m); // MyBox<String> -> &String -> &str

// String 实现了 Deref<Target=str>
let s = String::from("hello");
let slice: &str = &s; // 自动 deref

Deref 强制转换规则

  • T: Deref<Target=U> => &T 转换为 &U
  • T: DerefMut<Target=U> => &mut T 转换为 &mut U
  • T: Deref<Target=U> => &mut T 转换为 &U

Iterator

概述

Iterator trait 用于遍历序列。

定义

pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;

    // 许多默认方法
    // map, filter, fold, collect, etc.
}

示例

// 使用迭代器
let v = vec![1, 2, 3];
let mut iter = v.iter();
assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next(), Some(&2));

// 迭代器方法链
let v: Vec<i32> = (1..10)
    .filter(|x| x % 2 == 0)
    .map(|x| x * x)
    .collect();
// v = [4, 16, 36, 64]

// 自定义迭代器
struct Counter {
    count: u32,
}

impl Counter {
    fn new() -> Counter {
        Counter { count: 0 }
    }
}

impl Iterator for Counter {
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
        if self.count < 5 {
            self.count += 1;
            Some(self.count)
        } else {
            None
        }
    }
}

let sum: u32 = Counter::new().sum();
assert_eq!(sum, 15);

// for 循环使用 IntoIterator
let v = vec![1, 2, 3];
for item in v {  // v.into_iter()
    println!("{}", item);
}

三种迭代器方法

  • iter() => &T (不可变引用)
  • iter_mut() => &mut T (可变引用)
  • into_iter() => T (获取所有权)

总结对比表

Trait 用途 实现方式 关键点
Copy 隐式复制 派生 仅栈数据,快速
Clone 显式复制 派生或手动 可复制堆数据
Send 线程间转移所有权 自动 线程安全标记
Sync 线程间共享引用 自动 多线程共享
Drop 资源清理 手动 自动调用
Default 提供默认值 派生或手动 构造便利
Debug 调试输出 派生或手动 {:?} 格式
Display 用户输出 手动 {} 格式
PartialEq/Eq 相等比较 派生或手动 == 运算符
PartialOrd/Ord 排序比较 派生或手动 < 运算符
From/Into 类型转换 手动 值转换
AsRef/AsMut 引用转换 手动 廉价转换
Deref/DerefMut 解引用 手动 智能指针
Iterator 序列遍历 手动 next() 方法

实践建议

  1. 优先使用派生:能用 #[derive] 就不手动实现
  2. 理解所有权Copy vs Clone 的选择影响性能
  3. 线程安全:使用 Send + Sync 确保并发安全
  4. 智能转换:利用 From/IntoAsRef 让 API 更友好
  5. 迭代器优先:使用迭代器而非索引循环
  6. 实现一致性:实现 PartialEq 时考虑是否需要 Eq

参考资源

点赞

发表评论

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像

Title - Artist
0:00