8.5 运算符重载与 Deref 强制转换

Rust 不支持传统意义上的运算符重载,但允许通过实现特定的标准库 trait来定制类型对运算符的行为。此外,通过 DerefDerefMut trait,Rust 能自动将智能指针“解引用”为所指向的类型,并触发Deref 强制转换(Deref coercion),使自定义类型在使用上如同原生引用一样自然。

运算符重载

Rust 将运算符(如 +-*== 等)映射到对应的 trait。例如:

  • a + b → 调用 Add::add(a, b)
  • a == b → 调用 PartialEq::eq(&a, &b)
  • -a → 调用 Neg::neg(a)

要让自定义类型支持某个运算符,只需实现对应的 trait。

例如,为二维点实现加法:

use std::ops::Add;

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

impl Add for Point {
    type Output = Point;

    fn add(self, other: Point) -> Point {
        Point {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}

let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: 3, y: 4 };
let p3 = p1 + p2; // 调用 Add::add
println!("{:?}", p3); // Point { x: 4, y: 6 }

也可以实现不同类型之间的运算,例如点加偏移量:

struct Offset(i32, i32);

impl Add<Offset> for Point {
    type Output = Point;
    fn add(self, offset: Offset) -> Point {
        Point {
            x: self.x + offset.0,
            y: self.y + offset.1,
        }
    }
}

常见可重载的运算符 trait 包括:

  • 算术:Add, Sub, Mul, Div, Rem
  • 比较:PartialEq, Eq, PartialOrd, Ord
  • 位运算:BitAnd, BitOr, Shl, Shr
  • 索引:Index, IndexMut
  • 解引用:Deref, DerefMut

注意:Rust 不允许创建新运算符,只能重载已有运算符的语义。

Deref 强制转换

Deref trait 定义了如何将一个类型“解引用”为另一个类型。标准库中的 Box<T>Rc<T>StringVec<T> 都实现了 Deref

例如,String 实现了 Deref<Target = str>,因此可以将 &String 自动转换为 &str

fn takes_str(s: &str) { /* ... */ }

let s = String::from("hello");
takes_str(&s); // ✅ 自动将 &String 转为 &str

这种自动转换称为 Deref 强制转换。当函数期望 &T,但传入的是 &U,且 U 实现了 Deref<Target = T> 时,Rust 会自动插入 * 解引用操作,直到类型匹配。

自定义 Deref

你可以为自己的智能指针类型实现 Deref

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) -> &Self::Target {
        &self.0
    }
}

let x = 5;
let box_x = MyBox::new(x);
assert_eq!(5, *box_x); // *box_x 等价于 *(box_x.deref())

这里,*box_x 实际调用 box_x.deref(),返回 &i32,再解引用得到 i32

DerefMut 与可变解引用

若需可变访问,还需实现 DerefMut

use std::ops::{Deref, DerefMut};

impl<T> DerefMut for MyBox<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

这样就能在需要 &mut T 的地方传入 &mut MyBox<T>

使用场景与注意事项

  • 运算符重载应保持直观语义,避免违反用户预期(如用 + 表示连接而非加法);
  • Deref 强制转换主要用于智能指针类型,不应滥用(例如,不要为普通结构体实现 Deref 来“省略字段访问”);
  • Deref 应满足“继承”语义:MyBox<T> 应表现得像 T 本身,而不是仅仅提供便捷访问。

小结

通过实现标准 trait,Rust 允许安全地定制运算符行为;而 Deref 机制则使智能指针能无缝融入现有 API。这两者共同提升了自定义类型的表达力和易用性,同时保持语言的一致性和零成本抽象原则。正确使用它们,可以让库的接口更加自然、符合直觉。

#Rust 入门教程 分享于 1 周前

内容由 AI 创作和分享,仅供参考