Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Rust traits / Rust Trait

What you’ll learn / 你将学到: Traits — Rust’s answer to interfaces, abstract base classes, and operator overloading. You’ll learn how to define traits, implement them for your types, and use dynamic dispatch (dyn Trait) vs static dispatch (generics). For C++ developers: traits replace virtual functions, CRTP, and concepts. For C developers: traits are the structured way Rust does polymorphism.

Trait —— Rust 对接口、抽象基类和运算符重载的回答。你将学习如何定义 trait、为你的类型实现它们,以及如何使用动态分派(dyn Trait)与静态分派(泛型)。对于 C++ 开发者:trait 取代了虚函数、CRTP 和 concepts。对于 C 开发者:trait 是 Rust 实现多态的有组织方式。

  • Rust traits are similar to interfaces in other languages / Rust trait 类似于其他语言中的接口
  • - Traits define methods that must be defined by types that implement the trait.
    
  • - Traits define methods that must be defined by types that implement the trait. / Trait 定义了实现该 trait 的类型必须定义的方法。
    
fn main() {
    trait Pet {
        fn speak(&self);
    }
    struct Cat;
    struct Dog;
    impl Pet for Cat {
        fn speak(&self) {
            println!("Meow");
        }
    }
    impl Pet for Dog {
        fn speak(&self) {
            println!("Woof!")
        }
    }
    let c = Cat{};
    let d = Dog{};
-    c.speak();  // There is no "is a" relationship between Cat and Dog
+    c.speak();  // There is no "is a" relationship between Cat and Dog / Cat 和 Dog 之间没有“是一个(is a)”的关系
-    d.speak(); // There is no "is a" relationship between Cat and Dog
+    d.speak(); // There is no "is a" relationship between Cat and Dog / Cat 和 Dog 之间没有“是一个(is a)”的关系
}
// C++ - Inheritance-based polymorphism
+// C++ - 基于继承的多态
class Animal {
public:
-    virtual void speak() = 0;  // Pure virtual function
+    virtual void speak() = 0;  // Pure virtual function / 纯虚函数
    virtual ~Animal() = default;
};

- class Cat : public Animal {  // "Cat IS-A Animal"
+ class Cat : public Animal {  // "Cat IS-A Animal" / “Cat 是一个 Animal”
public:
    void speak() override {
        std::cout << "Meow" << std::endl;
    }
};

- void make_sound(Animal* animal) {  // Runtime polymorphism
+ void make_sound(Animal* animal) {  // Runtime polymorphism / 运行时多态
-    animal->speak();  // Virtual function call
+    animal->speak();  // Virtual function call / 虚函数调用
}
#![allow(unused)]
fn main() {
// Rust - Composition over inheritance with traits
+// Rust - 通过 trait 实现组合优于继承
trait Animal {
    fn speak(&self);
}

- struct Cat;  // Cat is NOT an Animal, but IMPLEMENTS Animal behavior
+ struct Cat;  // Cat is NOT an Animal, but IMPLEMENTS Animal behavior / Cat 不是 Animal,但实现了 Animal 的行为

- impl Animal for Cat {  // "Cat CAN-DO Animal behavior"
+ impl Animal for Cat {  // "Cat CAN-DO Animal behavior" / “Cat 可以执行 Animal 的行为”
    fn speak(&self) {
        println!("Meow");
    }
}

- fn make_sound<T: Animal>(animal: &T) {  // Static polymorphism
+ fn make_sound<T: Animal>(animal: &T) {  // Static polymorphism / 静态多态
-    animal.speak();  // Direct function call (zero cost)
+    animal.speak();  // Direct function call (zero cost) / 直接函数调用(零成本)
}
}
graph TD
-    subgraph "C++ Object-Oriented Hierarchy"
+    subgraph "C++ Object-Oriented Hierarchy / 面向对象层次结构"
-        CPP_ANIMAL["Animal<br/>(Abstract base class)"]
+        CPP_ANIMAL["Animal / 接口<br/>(Abstract base class / 抽象基类)"]
-        CPP_CAT["Cat : public Animal<br/>(IS-A relationship)"]
+        CPP_CAT["Cat : public Animal<br/>(IS-A relationship / “是一个”关系)"]
-        CPP_DOG["Dog : public Animal<br/>(IS-A relationship)"]
+        CPP_DOG["Dog : public Animal<br/>(IS-A relationship / “是一个”关系)"]
         
         CPP_ANIMAL --> CPP_CAT
         CPP_ANIMAL --> CPP_DOG
         
-        CPP_VTABLE["Virtual function table<br/>(Runtime dispatch)"]
+        CPP_VTABLE["Virtual function table / 虚函数表<br/>(Runtime dispatch / 运行时分派)"]
-        CPP_HEAP["Often requires<br/>heap allocation"]
+        CPP_HEAP["Often requires / 通常需要<br/>heap allocation / 堆分配"]
-        CPP_ISSUES["[ERROR] Deep inheritance trees<br/>[ERROR] Diamond problem<br/>[ERROR] Runtime overhead<br/>[ERROR] Tight coupling"]
+        CPP_ISSUES["[ERROR] Deep inheritance trees / 深层继承树<br/>[ERROR] Diamond problem / 菱形继承问题<br/>[ERROR] Runtime overhead / 运行时开销<br/>[ERROR] Tight coupling / 紧耦合"]
     end
     
-    subgraph "Rust Trait-Based Composition"
+    subgraph "Rust Trait-Based Composition / 基于 Trait 的组合"
-        RUST_TRAIT["trait Animal<br/>(Behavior definition)"]
+        RUST_TRAIT["trait Animal / 行为定义<br/>(Behavior definition)"]
-        RUST_CAT["struct Cat<br/>(Data only)"]
+        RUST_CAT["struct Cat<br/>(Data only / 仅数据)"]
-        RUST_DOG["struct Dog<br/>(Data only)"]
+        RUST_DOG["struct Dog<br/>(Data only / 仅数据)"]
         
-        RUST_CAT -.->|"impl Animal for Cat<br/>(CAN-DO behavior)"| RUST_TRAIT
+        RUST_CAT -.->|"impl Animal for Cat<br/>(CAN-DO behavior / “可以执行”行为)"| RUST_TRAIT
-        RUST_DOG -.->|"impl Animal for Dog<br/>(CAN-DO behavior)"| RUST_TRAIT
+        RUST_DOG -.->|"impl Animal for Dog<br/>(CAN-DO behavior / “可以执行”行为)"| RUST_TRAIT
         
-        RUST_STATIC["Static dispatch<br/>(Compile-time)"]
+        RUST_STATIC["Static dispatch / 静态分派<br/>(Compile-time / 编译时)"]
-        RUST_STACK["Stack allocation<br/>possible"]
+        RUST_STACK["Stack allocation / 栈分配<br/>possible / 是可能的"]
-        RUST_BENEFITS["[OK] No inheritance hierarchy<br/>[OK] Multiple trait impls<br/>[OK] Zero runtime cost<br/>[OK] Loose coupling"]
+        RUST_BENEFITS["[OK] No inheritance hierarchy / 无继承层次<br/>[OK] Multiple trait impls / 多个 trait 实现<br/>[OK] Zero runtime cost / 零运行时成本<br/>[OK] Loose coupling / 松耦合"]
     end
     
     style CPP_ISSUES fill:#ff6b6b,color:#000
     style RUST_BENEFITS fill:#91e5a3,color:#000
     style CPP_VTABLE fill:#ffa07a,color:#000
     style RUST_STATIC fill:#91e5a3,color:#000
#![allow(unused)]
fn main() {
use std::fmt::Display;
use std::ops::Add;

- // C++ template equivalent (less constrained)
+ // C++ template equivalent (less constrained) / C++ 模板等价物(约束较少)
// template<typename T>
// T add_and_print(T a, T b) {
- //     // No guarantee T supports + or printing
+ //     // No guarantee T supports + or printing / 无法保证 T 支持 + 或打印
- //     return a + b;  // Might fail at compile time
+ //     return a + b;  // Might fail at compile time / 可能会在编译时失败
// }

- // Rust - explicit trait bounds
+ // Rust - explicit trait bounds / Rust - 显式的 trait 限定
fn add_and_print<T>(a: T, b: T) -> T 
where 
    T: Display + Add<Output = T> + Copy,
{
-    println!("Adding {} + {}", a, b);  // Display trait
+    println!("Adding {} + {}", a, b);  // Display trait / Display trait 支持
-    a + b  // Add trait
+    a + b  // Add trait / Add trait 支持
}
}
graph TD
-    subgraph "Generic Constraints Evolution"
+    subgraph "Generic Constraints Evolution / 泛型约束的演进"
-        UNCONSTRAINED["fn process<T>(data: T)<br/>[ERROR] T can be anything"]
+        UNCONSTRAINED["fn process<T>(data: T)<br/>[ERROR] T can be anything / T 可以是任何东西"]
-        SINGLE_BOUND["fn process<T: Display>(data: T)<br/>[OK] T must implement Display"]
+        SINGLE_BOUND["fn process<T: Display>(data: T)<br/>[OK] T must implement Display / T 必须实现 Display"]
-        MULTI_BOUND["fn process<T>(data: T)<br/>where T: Display + Clone + Debug<br/>[OK] Multiple requirements"]
+        MULTI_BOUND["fn process<T>(data: T)<br/>where T: Display + Clone + Debug<br/>[OK] Multiple requirements / 多重需求"]
         
         UNCONSTRAINED --> SINGLE_BOUND
         SINGLE_BOUND --> MULTI_BOUND
     end
     
-    subgraph "Trait Bound Syntax"
+    subgraph "Trait Bound Syntax / Trait 限定语法"
-        INLINE["fn func<T: Trait>(param: T)"]
+        INLINE["fn func<T: Trait>(param: T) / 内联"]
-        WHERE_CLAUSE["fn func<T>(param: T)<br/>where T: Trait"]
+        WHERE_CLAUSE["fn func<T>(param: T)<br/>where T: Trait / Where 子句"]
-        IMPL_PARAM["fn func(param: impl Trait)"]
+        IMPL_PARAM["fn func(param: impl Trait) / impl 参数"]
         
-        COMPARISON["Inline: Simple cases<br/>Where: Complex bounds<br/>impl: Concise syntax"]
+        COMPARISON["Inline: Simple cases / 简单场景<br/>Where: Complex bounds / 复杂限定<br/>impl: Concise syntax / 简洁语法"]
     end
     
-    subgraph "Compile-time Magic"
+    subgraph "Compile-time Magic / 编译时魔法"
-        GENERIC_FUNC["Generic function<br/>with trait bounds"]
+        GENERIC_FUNC["Generic function / 泛型函数<br/>with trait bounds / 带有 trait 限定"]
-        TYPE_CHECK["Compiler verifies<br/>trait implementations"]
+        TYPE_CHECK["Compiler verifies / 编译器验证<br/>trait implementations / trait 实现"]
-        MONOMORPH["Monomorphization<br/>(Create specialized versions)"]
+        MONOMORPH["Monomorphization / 单态化<br/>(Create specialized versions / 创建专用版本)"]
-        OPTIMIZED["Fully optimized<br/>machine code"]
+        OPTIMIZED["Fully optimized / 完全优化<br/>machine code / 机器码"]
         
         GENERIC_FUNC --> TYPE_CHECK
         TYPE_CHECK --> MONOMORPH
         MONOMORPH --> OPTIMIZED
         
-        EXAMPLE["add_and_print::<i32><br/>add_and_print::<f64><br/>(Separate functions generated)"]
+        EXAMPLE["add_and_print::<i32><br/>add_and_print::<f64><br/>(Separate functions generated / 生成了独立的函数)"]
         MONOMORPH --> EXAMPLE
     end
     
     style UNCONSTRAINED fill:#ff6b6b,color:#000
     style SINGLE_BOUND fill:#ffa07a,color:#000
     style MULTI_BOUND fill:#91e5a3,color:#000
     style OPTIMIZED fill:#91e5a3,color:#000
  • In C++, you overload operators by writing free functions or member functions with special names (operator+, operator<<, operator[], etc.). In Rust, every operator maps to a trait in std::ops (or std::fmt for output). You implement the trait instead of writing a magic-named function.
  • 在 C++ 中,你通过编写具有特殊名称(operator+operator<<operator[] 等)的全局函数或成员函数来重载运算符。在 Rust 中,每个运算符都映射到 std::ops(或用于输出的 std::fmt)中的一个 trait。你实现该 trait,而不是编写具有魔法名称的函数。
// C++: operator overloading as a member or free function
+// C++:作为成员或全局函数的运算符重载
struct Vec2 {
    double x, y;
    Vec2 operator+(const Vec2& rhs) const {
        return {x + rhs.x, y + rhs.y};
    }
};

Vec2 a{1.0, 2.0}, b{3.0, 4.0};
- Vec2 c = a + b;  // calls a.operator+(b)
+ Vec2 c = a + b;  // calls / 调用 a.operator+(b)
#![allow(unused)]
fn main() {
use std::ops::Add;

#[derive(Debug, Clone, Copy)]
struct Vec2 { x: f64, y: f64 }

impl Add for Vec2 {
-    type Output = Vec2;                     // Associated type — the result of +
+    type Output = Vec2;                     // Associated type / 关联类型 —— + 的结果
    fn add(self, rhs: Vec2) -> Vec2 {
        Vec2 { x: self.x + rhs.x, y: self.y + rhs.y }
    }
}

let a = Vec2 { x: 1.0, y: 2.0 };
let b = Vec2 { x: 3.0, y: 4.0 };
- let c = a + b;  // calls <Vec2 as Add>::add(a, b)
+ let c = a + b;  // calls / 调用 <Vec2 as Add>::add(a, b)
- println!("{c:?}"); // Vec2 { x: 4.0, y: 6.0 }
+ println!("{c:?}"); // 结果:Vec2 { x: 4.0, y: 6.0 }
}

-| Aspect | C++ | Rust | +| Aspect / 维度 | C++ | Rust | |––––|—–|——| -| Mechanism | Magic function names (operator+) | Implement a trait (impl Add for T) | +| Mechanism / 机制 | Magic function names / 魔法函数名 (operator+) | Implement a trait / 实现 Trait (impl Add for T) | -| Discovery | Grep for operator+ or read the header | Look at trait impls — IDE support excellent | +| Discovery / 发现方式 | Grep for operator+ or read the header | Look at trait impls / 查看 Trait 实现 —— IDE 支持极佳 | -| Return type | Free choice | Fixed by the Output associated type | +| Return type / 返回类型 | Free choice / 自由选择 | Fixed by the Output associated type / 由 Output 关联类型固定 | -| Receiver | Usually takes const T& (borrows) | Takes self by value (moves!) by default | +| Receiver / 接收端 | Usually takes const T& (borrows) | Takes self by value (moves! / 移动) by default | -| Symmetry | Can write impl operator+(int, Vec2) | Must add impl Add<Vec2> for i32 (foreign trait rules apply) | +| Symmetry / 对称性 | Can write impl operator+(int, Vec2) | Must add impl Add<Vec2> for i32 | -| << for printing | operator<<(ostream&, T) — overload for any stream | impl fmt::Display for T — one canonical to_string representation | +| << for printing / 用于打印的 << | operator<<(ostream&, T) | impl fmt::Display for T — 规范的字符串表示 |

  • In Rust, Add::add(self, rhs) takes self by value. For Copy types (like Vec2 above, which derives Copy) this is fine — the compiler copies. But for non-Copy types, + consumes the operands:
  • 在 Rust 中,Add::add(self, rhs)获取 self。对于 Copy 类型(例如上面的 Vec2,它派生了 Copy),这没问题 —— 编译器会进行拷贝。但对于非 Copy 类型,+消耗操作数:
#![allow(unused)]
fn main() {
let s1 = String::from("hello ");
let s2 = String::from("world");
- let s3 = s1 + &s2;  // s1 is MOVED into s3!
+ let s3 = s1 + &s2;  // s1 is MOVED into s3! / s1 移动到了 s3 中!
- // println!("{s1}");  // ❌ Compile error: value used after move
+ // println!("{s1}");  // ❌ Compile error / 编译错误:值在移动后被使用
- println!("{s2}");     // ✅ s2 was only borrowed (&s2)
+ println!("{s2}");     // ✅ s2 only borrowed / s2 只是被借用 (&s2)
}
  • This is why String + &str works but &str + &str does not — Add is only implemented for String + &str, consuming the left-hand String to reuse its buffer. This has no C++ analogue: std::string::operator+ always creates a new string.
  • 这就是为什么 String + &str 有效但 &str + &str 无效的原因 —— Add 仅为 String + &str 实现,消耗左侧的 String 以重用其缓冲区。这没有 C++ 的对应物:std::string::operator+ 总是创建一个新字符串。

-| C++ Operator | Rust Trait | Notes | +| C++ Operator / 运算符 | Rust Trait | Notes / 说明 | |———––|———–|—––| -| operator+ | std::ops::Add | Output associated type | +| operator+ | std::ops::Add | Output associated type / 关联类型 | -| operator- | std::ops::Sub | | -| operator- | std::ops::Sub | | -| operator* | std::ops::Mul | Not pointer deref — that’s Deref | +| operator* | std::ops::Mul | Not pointer deref / 不是指针解引用 —— 那是 Deref | -| operator/ | std::ops::Div | | -| operator/ | std::ops::Div | | -| operator% | std::ops::Rem | | -| operator/ | std::ops::Div | | -| operator% | std::ops::Rem | | -| operator- (unary) | std::ops::Neg | | -| operator- (unary) | std::ops::Neg | | -| operator! / operator~ | std::ops::Not | Rust uses ! for both logical and bitwise NOT (no ~ operator) | +| operator! / operator~ | std::ops::Not | Rust treats ! as both / Rust 将 ! 同时用于逻辑和位取反 | -| operator&, \|, ^ | BitAnd, BitOr, BitXor | | -| operator&, \|, ^ | BitAnd, BitOr, BitXor | | -| operator<<, >> (shift) | Shl, Shr | NOT stream I/O! | +| operator<<, >> (shift) | Shl, Shr | NOT stream I/O! / 不是流 I/O! | -| operator+= | std::ops::AddAssign | Takes &mut self (not self) | +| operator+= | std::ops::AddAssign | Takes &mut self / 使用 &mut self | -| operator[] | std::ops::Index / IndexMut | Returns &Output / &mut Output | +| operator[] | std::ops::Index / IndexMut | | -| operator() | Fn / FnMut / FnOnce | Closures implement these; you cannot impl Fn directly | +| operator() | Fn / FnMut / FnOnce | Closures implement these / 闭包实现了这些 | -| operator== | PartialEq (+ Eq) | In std::cmp, not std::ops | +| operator== | PartialEq (+ Eq) | In std::cmp | -| operator< | PartialOrd (+ Ord) | In std::cmp | +| operator< | PartialOrd (+ Ord) | In std::cmp | -| operator<< (stream) | fmt::Display | println!("{}", x) | -| operator<< (debug) | fmt::Debug | println!("{:?}", x) | -| operator bool | No direct equivalent | Use impl From<T> for bool or a named method like .is_empty() | +| operator bool | No equivalent / 无等价物 | Use From/Into or named method / 使用 From/Into 或命名方法 | -| operator T() (implicit conversion) | No implicit conversions | Use From/Into traits (explicit) | +| operator T() (implicit) | No implicit / 无隐式转换 | Use From/Into / 使用显式的 From/Into |

    1. No implicit conversions: C++ operator int() can cause silent, surprising casts. Rust has no implicit conversion operators — use From/Into and call .into() explicitly.
    1. No implicit conversions / 无隐式转换:C++ operator int() 可能会导致静默且令人惊讶的转换。Rust 没有隐式转换运算符 —— 请使用 From/Into 并显式调用 .into()
    1. No overloading && / ||: C++ allows it (breaking short-circuit semantics!). Rust does not.
    1. No overloading && / || / 不允许重载 && / ||:C++ 允许这样做(会破坏短路语义!)。Rust 不允许。
    1. No overloading =: Assignment is always a move or copy, never user-defined. Compound assignment (+=) IS overloadable via AddAssign, etc.
    1. No overloading = / 不允许重载 =:赋值始终是移动或拷贝,永远不能由用户定义。复合赋值(+=)可以通过 AddAssign 等进行重载。
    1. No overloading ,: C++ allows operator,() — one of the most infamous C++ footguns. Rust does not.
    1. No overloading , / 不允许重载 ,:C++ 允许 operator,() —— 这是 C++ 最臭名昭著的搬起石头砸自己脚的特性之一。Rust 不允许。
    1. No overloading & (address-of): Another C++ footgun (std::addressof exists to work around it). Rust’s & always means “borrow.”
    1. No overloading & (address-of) / 不允许重载 &(取地址):另一个 C++ 的坑点(std::addressof 的存在就是为了绕过它)。Rust 的 & 始终表示“借用”。
    1. Coherence rules: You can only implement Add<Foreign> for your own type, or Add<YourType> for a foreign type — never Add<Foreign> for Foreign. This prevents conflicting operator definitions across crates.
    1. Coherence rules / 一致性规则:你只能为自己的类型实现 Add<Foreign>,或为外部类型实现 Add<YourType> —— 永远不能为 Foreign 实现 Add<Foreign>。这可以防止各 crate 之间出现冲突的运算符定义。
  • Bottom line: In C++, operator overloading is powerful but largely unregulated — you can overload almost anything, including comma and address-of, and implicit conversions can trigger silently. Rust gives you the same expressiveness for arithmetic and comparison operators via traits, but blocks the historically dangerous overloads and forces all conversions to be explicit.

  • 底线:在 C++ 中,运算符重载功能强大但很大程度上不受监管 —— 你几乎可以重载任何东西,包括逗号和取地址符,而且隐式转换可能会静默触发。Rust 通过 trait 为你提供了相同的算术和比较运算符表达能力,但阻止了历史上危险的重载,并强制所有转换都是显式的。


    • Rust allows implementing a user defined trait on even built-in types like u32 in this example. However, either the trait or the type must belong to the crate
    • Rust 允许在甚至像本例中的 u32 这样的内置类型上实现用户定义的 trait。但是,trait 或类型中必须有一个属于当前的 crate。
trait IsSecret {
  fn is_secret(&self);
}
- // The IsSecret trait belongs to the crate, so we are OK
+ // The IsSecret trait belongs to the crate, so we are OK / IsSecret trait 属于本 crate,所以没问题
impl IsSecret for u32 {
  fn is_secret(&self) {
      if *self == 42 {
          println!("Is secret of life");
      }
  }
}

fn main() {
  42u32.is_secret();
  43u32.is_secret();
}
    • Traits support interface inheritance and default implementations
    • Trait 支持接口继承和默认实现
trait Animal {
-  // Default implementation
+  // Default implementation / 默认实现
  fn is_mammal(&self) -> bool {
    true
  }
}
trait Feline : Animal {
-  // Default implementation
+  // Default implementation / 默认实现
  fn is_feline(&self) -> bool {
    true
  }
}

struct Cat;
- // Use default implementations. Note that all traits for the supertrait must be individually implemented
+ // Use default implementations. Note that all traits for the supertrait must be individually implemented / 使用默认实现。注意父 trait 的所有 trait 都必须分别实现。
impl Feline for Cat {}
impl Animal for Cat {}
fn main() {
  let c = Cat{};
  println!("{} {}", c.is_mammal(), c.is_feline());
}

  • 🟡 Intermediate
  • 🟡 Intermediate / 中级
    • Implement a Log trait with a single method called log() that accepts a u64
    • 实现一个 Log trait,带有一个名为 log() 的方法,该方法接受一个 u64
  • - Implement two different loggers ```SimpleLogger``` and ```ComplexLogger``` that implement the ```Log trait```. One should output "Simple logger" with the ```u64``` and the other should output "Complex logger" with the ```u64``` 
    
  • - 实现两个不同的记录器 ```SimpleLogger``` 和 ```ComplexLogger```,它们实现 ```Log trait```。一个应输出 "Simple logger" 及其 ```u64``` 对应的值,另一个应输出 "Complex logger" 及其 ```u64``` 对应的值。
    
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
trait Log {
    fn log(&self, value: u64);
}

struct SimpleLogger;
struct ComplexLogger;

impl Log for SimpleLogger {
    fn log(&self, value: u64) {
        println!("Simple logger: {value}");
    }
}

impl Log for ComplexLogger {
    fn log(&self, value: u64) {
        println!("Complex logger: {value} (hex: 0x{value:x}, binary: {value:b})");
    }
}

fn main() {
    let simple = SimpleLogger;
    let complex = ComplexLogger;
    simple.log(42);
    complex.log(42);
}
- // Output:
+ // Output / 输出:
// Simple logger: 42
// Complex logger: 42 (hex: 0x2a, binary: 101010)

#[derive(Debug)]
struct Small(u32);
#[derive(Debug)]
struct Big(u32);
trait Double {
    type T;
    fn double(&self) -> Self::T;
}

impl Double for Small {
-    type T = Big;
+    type T = Big; // 指定关联类型
    fn double(&self) -> Self::T {
        Big(self.0 * 2)
    }
}
fn main() {
    let a = Small(42);
    println!("{:?}", a.double());
}
    • impl can be used with traits to accept any type that implements a trait
    • impl 关键字可以与 trait 一起使用,以接受实现该 trait 的任何类型
trait Pet {
    fn speak(&self);
}
struct Dog {}
struct Cat {}
impl Pet for Dog {
    fn speak(&self) {println!("Woof!")}
}
impl Pet for Cat {
    fn speak(&self) {println!("Meow")}
}
fn pet_speak(p: &impl Pet) {
    p.speak();
}
fn main() {
    let c = Cat {};
    let d = Dog {};
    pet_speak(&c);
    pet_speak(&d);
}
    • impl can be also be used be used in a return value
    • impl 关键字也可以用于返回值中
trait Pet {}
struct Dog;
struct Cat;
impl Pet for Cat {}
impl Pet for Dog {}
fn cat_as_pet() -> impl Pet {
    let c = Cat {};
    c
}
fn dog_as_pet() -> impl Pet {
    let d = Dog {};
    d
}
fn main() {
-    let p = cat_as_pet();
+    let _p = cat_as_pet();
-    let d = dog_as_pet();
+    let _d = dog_as_pet();
}

    • Dynamic traits can be used to invoke the trait functionality without knowing the underlying type. This is known as type erasure
    • 动态 trait 可用于在不知道底层类型的情况下调用 trait 功能。这被称为 类型擦除(type erasure)
trait Pet {
    fn speak(&self);
}
struct Dog {}
struct Cat {x: u32}
impl Pet for Dog {
    fn speak(&self) {println!("Woof!")}
}
impl Pet for Cat {
    fn speak(&self) {println!("Meow")}
}
- fn pet_speak(p: &dyn Pet) {
+ fn pet_speak(p: &dyn Pet) { // 动态分派
    p.speak();
}
fn main() {
    let c = Cat {x: 42};
    let d = Dog {};
    pet_speak(&c);
    pet_speak(&d);
}

  • These three approaches all achieve polymorphism but with different trade-offs:
  • 这三种方法都能实现多态,但具有不同的权衡:

-| Approach | Dispatch | Performance | Heterogeneous collections? | When to use | +| Approach / 方法 | Dispatch / 分派 | Performance / 性能 | Heterogeneous / 异构集合? | When to use / 何时使用 | |–––––|–––––|———––|—————————|———––| -| impl Trait / generics | Static (monomorphized) | Zero-cost — inlined at compile time | No — each slot has one concrete type | Default choice. Function arguments, return types | -| dyn Trait | Dynamic (vtable) | Small overhead per call (~1 pointer indirection) | Yes — Vec<Box<dyn Trait>> | When you need mixed types in a collection, or plugin-style extensibility | -| enum | Match | Zero-cost — known variants at compile time | Yes — but only known variants | When the set of variants is closed and known at compile time | +| impl Trait / 泛型 | Static / 静态 (monomorphized) | Zero-cost / 零成本 —— 编译时内联 | No / 否 —— 每个位置只能有一种具体类型 | 默认选择。函数参数、返回类型 | +| dyn Trait | Dynamic / 动态 (vtable) | Small overhead / 微小开销 | Yes / 是 —— Vec<Box<dyn Trait>> | 需要混合类型或插件式扩展时 | +| enum | Match | Zero-cost / 零成本 | Yes / 是 —— 但仅限于已知的变体 | 变体集合是封闭的且在编译时已知时 |

#![allow(unused)]
fn main() {
trait Shape {
    fn area(&self) -> f64;
}
struct Circle { radius: f64 }
struct Rect { w: f64, h: f64 }
impl Shape for Circle { fn area(&self) -> f64 { std::f64::consts::PI * self.radius * self.radius } }
impl Shape for Rect   { fn area(&self) -> f64 { self.w * self.h } }

- // Static dispatch — compiler generates separate code for each type
+ // Static dispatch — compiler generates separate code for each type / 静态分派 —— 编译器为每种类型生成独立代码
fn print_area(s: &impl Shape) { println!("{}", s.area()); }

- // Dynamic dispatch — one function, works with any Shape behind a pointer
+ // Dynamic dispatch — one function, works with any Shape behind a pointer / 动态分派 —— 一个函数即可处理指针后的任何 Shape
fn print_area_dyn(s: &dyn Shape) { println!("{}", s.area()); }

- // Enum — closed set, no trait needed
+ // Enum — closed set, no trait needed / 枚举 —— 封闭集合,不需要 trait
enum ShapeEnum { Circle(f64), Rect(f64, f64) }
impl ShapeEnum {
    fn area(&self) -> f64 {
        match self {
            ShapeEnum::Circle(r) => std::f64::consts::PI * r * r,
            ShapeEnum::Rect(w, h) => w * h,
        }
    }
}
}
  • For C++ developers: impl Trait is like C++ templates (monomorphized, zero-cost). dyn Trait is like C++ virtual functions (vtable dispatch). Rust enums with match are like std::variant with std::visit — but exhaustive matching is enforced by the compiler.

  • For C++ developers / C++ 开发者注意: impl Trait 类似于 C++ 模板(单态化、零成本)。dyn Trait 类似于 C++ 虚函数(虚表分派)。带有 match 的 Rust 枚举类似于 std::variantstd::visit 的结合 —— 但 Rust 编译器会强制执行穷尽匹配。

  • Rule of thumb: Start with impl Trait (static dispatch). Reach for dyn Trait only when you need heterogeneous collections or can’t know the concrete type at compile time. Use enum when you own all the variants.

  • 经验法则:从 impl Trait(静态分派)开始。仅当你需要异构集合或在编译时无法确定具体类型时,才求助于 dyn Trait。当你拥有所有变体时,使用 enum