【Rust】 5. Trait 与运算符重载

发布于:2025-08-30 ⋅ 阅读:(22) ⋅ 点赞:(0)

一、Trait 基础概念

Trait 是 Rust 中定义共享行为的机制,类似于其他语言的接口,但有重要区别。

  1. 基本定义

trait Shape {
    fn area(&self) -> f64;
}
  1. 方法参数说明
  • &self 等价于 self: &Self

  • &mut self 等价于 self: &mut Self

  • self 等价于 self: Self

  1. 实现示例

struct Circle {
    radius: f64,
}

impl Shape for Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * self.radius * self.radius
    }
}

二、方法类型

  1. 实例方法

impl Circle {
    fn get_radius(&self) -> f64 {
        self.radius
    }
}

2.2 静态方法


impl Circle {
    fn get_area(this: &Self) -> f64 {
        std::f64::consts::PI * this.radius * this.radius
    }
}

三、扩展方法

可以为现有类型(包括内置类型)添加方法:


trait Double {
    fn double(&self) -> Self;
}

impl Double for i32 {
    fn double(&self) -> i32 {
        self * 2
    }
}

四、泛型约束与分发机制

  1. 静态分发(编译时)

fn my_print<T: ToString>(v: T) {
    v.to_string();
}

4.2 动态分发(运行时)


fn dynamic_dispatch(animal: &dyn Animal) {
    animal.make_sound();
}

五、一致性原则(孤儿规则)

  • Impl 块必须与 trait 或类型在同一个 crate 中

  • 防止外部 crate 为外部类型实现外部 trait

六、Trait 与接口的区别

Trait 不是具体类型,不能直接用作:

  • 参数类型

  • 返回值类型

  • 变量类型

七、Derive 派生宏

自动实现常见 trait:


#[derive(Debug, Clone, PartialEq)]
struct Foo {
    data: i32,
}

支持的 trait:Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord 等

八、标记特质(Marker Traits)

标记特质是不含任何方法的特殊 trait,用于在类型系统中标记类型属性:

  1. 常见标记特质
  • Copy: 标记类型可以按位复制

  • Send: 标记类型可以安全地在线程间传递所有权

  • Sync: 标记类型可以安全地在线程间共享引用

  • Sized: 标记类型在编译时大小已知(几乎所有类型都自动实现)


// 手动实现标记特质
unsafe impl Send for MyType {}
unsafe impl Sync for MyType {}
  1. 自动 trait

Send 和 Sync 是自动 trait,满足条件时会自动实现:


// 这个结构体会自动实现 Send 和 Sync
#[derive(Debug)]
struct SafeData {
    value: i32,
}

// 包含裸指针的结构体需要手动实现
struct UnsafeData {
    ptr: *mut i32,
}

// 需要 unsafe 因为我们要保证线程安全
unsafe impl Send for UnsafeData {}
unsafe impl Sync for UnsafeData {}

九、类型计算与关联类型

  1. 关联类型(Associated Types)

在 trait 中定义类型占位符:


trait Graph {
    type Node;  // 关联类型
    type Edge;
    
    fn nodes(&self) -> Vec<Self::Node>;
    fn edges(&self, node: &Self::Node) -> Vec<Self::Edge>;
}

struct MyGraph;

impl Graph for MyGraph {
    type Node = u32;
    type Edge = (u32, u32);
    
    fn nodes(&self) -> Vec<Self::Node> {
        vec![1, 2, 3]
    }
    
    fn edges(&self, node: &Self::Node) -> Vec<Self::Edge> {
        match node {
            1 => vec![(1, 2), (1, 3)],
            2 => vec![(2, 3)],
            _ => vec![],
        }
    }
}
  1. 泛型关联类型(GATs)

Rust 1.65+ 支持泛型关联类型:


trait Factory {
    type Product<T>;
    
    fn create<T>(&self, value: T) -> Self::Product<T>;
}

struct MyFactory;

impl Factory for MyFactory {
    type Product<T> = Option<T>;
    
    fn create<T>(&self, value: T) -> Self::Product<T> {
        Some(value)
    }
}

十、标准库重要 Trait

  1. Display vs Debug
  • Display: 用户友好输出,需手动实现

  • Debug: 调试输出,可自动派生

  1. ToString

自动为实现了 Display 的类型提供 to_string() 方法
3. 相等性比较

  • PartialEq: 部分相等比较

  • Eq: 完全相等比较(继承自 PartialEq)

  1. 排序比较
  • PartialOrd: 部分排序比较

  • Ord: 完全排序比较

  1. Clone 与 Copy
  • Clone: 显式深度拷贝

  • Copy: 标记 trait,表示按位拷贝

  1. Drop

资源清理 trait:


impl Drop for Point {
    fn drop(&mut self) {
        println!("Dropping point");
    }
}
  1. 类型转换 Trait
  • From/Into

impl From<i32> for Point {
    fn from(x: i32) -> Self {
        Point { x, y: 0 }
    }
}
  • TryFrom/TryInto

可失败的类型转换:


impl TryFrom<String> for Point {
    type Error = ParseError;
    
    fn try_from(s: String) -> Result<Self, Self::Error> {
        // 解析逻辑
    }
}
  • FromStr

字符串解析:


impl FromStr for Point {
    type Err = ParseError;
    
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // 解析逻辑
    }
}
  1. AsRef

引用转换 trait:


fn takes_asref_str<S: AsRef<str>>(s: S) {
    let s: &str = s.as_ref();
}

十一、运算符重载

通过实现特定 trait 实现运算符重载:

  1. 算术运算符
  • Add: +

  • Sub: -

  • Mul: *

  • Div: /


use std::ops::Add;

impl Add for Point {
    type Output = Self;
    
    fn add(self, other: Self) -> Self {
        Point {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}
  1. 比较运算符

通过实现 PartialEq, PartialOrd 等 trait 实现 ==, !=, <, > 等运算符
3. 索引运算符

通过实现 Index 和 IndexMut trait 实现 [] 运算符

十二、高级 Trait 特性

  1. Supertraits(父 trait)

一个 trait 可以要求实现者同时实现其他 trait:


trait Circle: Shape + Display {
    fn radius(&self) -> f64;
}
  1. Trait 对象与对象安全

只有对象安全的 trait 才能用作 trait 对象 (dyn Trait):

  • 方法不能返回 Self

  • 方法不能有泛型参数


// 对象安全的 trait
trait Draw {
    fn draw(&self);
}

// 非对象安全的 trait(因为有泛型方法)
trait Processor {
    fn process<T>(&self, value: T);
}

十三、类型计算示例

  1. 使用泛型进行类型计算

trait Length {
    type Output;
    
    fn length(self) -> Self::Output;
}

impl Length for &str {
    type Output = usize;
    
    fn length(self) -> Self::Output {
        self.len()
    }
}

impl<T> Length for Vec<T> {
    type Output = usize;
    
    fn length(self) -> Self::Output {
        self.len()
    }
}
  1. 条件 trait 实现

use std::fmt::Display;

struct Wrapper<T>(T);

// 只为实现了 Display 的类型实现 Debug
impl<T: Display> std::fmt::Debug for Wrapper<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Wrapper({})", self.0)
    }
}

十四、总结

  1. Trait 是 Rust 多态性的核心机制

  2. 标记特质用于在类型系统中标记类型属性

  3. 关联类型和 GATs 支持高级类型计算

  4. 区分静态分发与动态分发的适用场景

  5. 遵循一致性原则(孤儿规则)

  6. 合理使用 derive 派生宏减少样板代码

  7. 标准库 trait 为运算符重载和类型转换提供基础

  8. Trait 对象 (dyn Trait) 提供运行时多态性

  9. Supertraits 支持 trait 组合和继承

通过熟练掌握 trait 系统,可以编写出灵活、高效且类型安全的 Rust 代码。


网站公告

今日签到

点亮在社区的每一天
去签到