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

English Original

Trait:Rust 的接口机制

你将学到什么: Trait 与 C# 接口的对应关系、默认方法实现、trait object (dyn Trait) 与泛型约束 (impl Trait) 的区别,以及标准库中的常见 trait。

难度: 中级

Trait 是 Rust 中定义共享行为的方式。概念上它们和 C# 的接口非常像,但被用在了一种更加灵活且解耦的方式上。


Trait vs 接口

在 C# 中,一个类(Class)必须明确声明它实现了某个接口。而在 Rust 中,你可以为任意类型实现一个 Trait,甚至是那些不是由你定义的类型(但需遵循一定规则)。

C# 接口

public interface IShape {
    double Area();
}

public class Circle : IShape {
    public double Radius { get; set; }
    public double Area() => Math.PI * Radius * Radius;
}

Rust Trait

#![allow(unused)]
fn main() {
trait Shape {
    fn area(&self) -> f64;
}

struct Circle { radius: f64 }

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

默认实现

就像现代 C# (8.0+) 一样,Rust 的 Trait 可以拥有默认的方法实现。

#![allow(unused)]
fn main() {
trait Summary {
    fn summarize(&self) -> String {
        String::from("(点击阅读更多...)")
    }
}
}

泛型与 Trait 约束 (Trait Bounds)

你可以使用 Trait 来限制哪些类型可以被传入一个泛型函数。

#![allow(unused)]
fn main() {
fn print_area<T: Shape>(shape: &T) {
    println!("该形状的面积是 {}", shape.area());
}
}

静态分发 (Static Dispatch): 编译器会为你调用的每一种具体类型,生成一个专门的 print_area 函数副本。这非常快(零运行时成本),但可能会增加二进制文件的体积。


Trait Object (dyn Trait)

有些时候,你需要在同一个集合中存放不同的类型。由于 Rust 需要在编译期知道类型的大小,你必须通过指针(如 Box&)来使用 Trait Object。

#![allow(unused)]
fn main() {
let shapes: Vec<Box<dyn Shape>> = vec![
    Box::new(Circle { radius: 1.0 }),
    Box::new(Square { side: 2.0 }),
];
}

动态分发 (Dynamic Dispatch): 与泛型不同,这会在运行时通过一个“虚函数表 (vtable)”来寻找对应的正确方法。虽然速度稍慢,但它支持异类集合(Heterogeneous Collection)。


C# 开发者总结表

概念C# 对应物Rust 现实
接口interface Itrait T
实现class C : Iimpl T for C
泛型where T : I<T: T> (静态分发)
多态List<I>Vec<Box<dyn T>> (动态分发)
隐式实现不可以可以 (但需遵循孤儿规则)

练习:实现一个 Trait

挑战: 定义一个包含 draw 方法的 Drawable trait。为 Point 结构体实现它,并编写一个能够接收 impl Drawable 参数的渲染函数。

#![allow(unused)]
fn main() {
trait Drawable {
    fn draw(&self);
}

struct Point { x: i32, y: i32 }

impl Drawable for Point {
    fn draw(&self) { println!("({}, {})", self.x, self.y); }
}

fn render(item: impl Drawable) {
    item.draw();
}
}

关键理解: Trait 是将 Rust 代码粘合在一起的关键。它们提供了如同接口般的抽象能力,同时又没有继承体系中僵化的层级链。