Rust: Rust 特征(Traits):定义共享行为
最后更新:2026-08-26
特征(trait)是 Rust 的"接口"——它定义了一组方法签名,不同类型可以实现同一个 trait,从而共享相同的行为约定。
如果说具体类型是"这东西是什么",那 trait 就是"这东西能做什么"。就像 USB-C 接口——无论是手机、笔记本还是平板,只要支持 USB-C,插上就能充电。设备内部千差万别,但对外承诺的行为是一致的。
1. 你将学到
- trait 的定义语法
trait Name { fn method(&self); }与为类型实现 trait - 默认方法——在 trait 定义中提供默认实现,实现者可以选择覆盖
- 派生 trait(Derivable Trait):
#[derive(Debug, Clone, Copy, PartialEq)] - trait 作为参数:
impl Trait语法与泛型约束T: Trait - trait 对象
dyn Trait——运行时动态分发 - trait 继承(超 trait):一个 trait 继承另一个 trait 的方法
2. 概念图解
以下 Mermaid 图展示 trait 从定义到为各类型实现再到多态调用的完整关系链:
graph LR
A["trait UsbCCharge"] -->|定义行为契约| B["fn charge(&self)"]
A --> C["fn voltage(&self) -> u32"]
A --> D["fn charge_time(&self) -> String"]
B --> E["impl for Phone<br/>charge: 18W"]
B --> F["impl for Laptop<br/>charge: 65W"]
B --> G["impl for Tablet<br/>charge: 30W"]
E --> H["phone.charge()"]
F --> I["laptop.charge()"]
G --> J["tablet.charge()"]
H --> K["多态调用<br/>同一 trait 接口<br/>不同类型行为各异"]
I --> K
J --> K
3. 通用充电器的故事
(1) 痛苦:每个设备都有自己的充电方法
Leo (Leo) 是一家电子产品公司的架构师。公司产品线越来越多:Phone、Laptop、Tablet,每个设备都有自己的充电逻辑。
最开始他为每个设备单独写充电代码:
struct Phone;
struct Laptop;
struct Tablet;
impl Phone {
fn charge(&self) {
println!("Phone: charging via USB-C at 18W");
}
}
impl Laptop {
fn charge(&self) {
println!("Laptop: charging via USB-C at 65W");
}
}
impl Tablet {
fn charge(&self) {
println!("Tablet: charging via USB-C at 30W");
}
}
fn main() {
let phone = Phone;
let laptop = Laptop;
let tablet = Tablet;
phone.charge();
laptop.charge();
tablet.charge();
}
三个结构体各自有一个
charge方法,方法名相同,签名相同,但彼此之间没有任何"约定"关系。如果要写一个"通用充电站"函数来给任意设备充电——做不到。每个设备类型都是独立的,没有共同的抽象层。
(2) 更复杂的需求:批量充电
产品经理要求开发一个"通用充电站",能同时给多个不同类型的设备充电:
// 这种代码无法编译——参数类型不确定
// fn charge_all_devices(devices: ???) {
// for device in devices {
// device.charge();
// }
// }
没有 trait,charge_all_devices 无法接受混合类型的集合。要么为每种类型重载一个函数,要么放弃。
(3) Rust trait 的方案
// Define a trait: a shared behavior contract
trait UsbCCharge {
fn charge(&self);
fn voltage(&self) -> u32 { 18 } // Default method with default voltage
}
struct Phone;
struct Laptop;
struct Tablet;
impl UsbCCharge for Phone {
fn charge(&self) {
println!("Phone: charging via USB-C at {}W", self.voltage());
}
// voltage() uses the default (18W)
}
impl UsbCCharge for Laptop {
fn charge(&self) {
println!("Laptop: charging via USB-C at {}W", self.voltage());
}
fn voltage(&self) -> u32 { 65 } // Override default
}
impl UsbCCharge for Tablet {
fn charge(&self) {
println!("Tablet: charging via USB-C at {}W", self.voltage());
}
fn voltage(&self) -> u32 { 30 } // Override default
}
// Generic function: accepts any type that implements UsbCCharge
fn charge_device<T: UsbCCharge>(device: &T) {
device.charge();
}
fn main() {
charge_device(&Phone);
charge_device(&Laptop);
charge_device(&Tablet);
}
输出:
Phone: charging via USB-C at 18W
Laptop: charging via USB-C at 65W
Tablet: charging via USB-C at 30W
trait UsbCCharge定义了一个"充电行为契约"。任何实现了这个 trait 的类型都可以被charge_device接受。voltage()有默认实现(18W),但 Laptop 和 Tablet 选择覆盖它。这就是 trait 的核心价值——定义共享行为,允许差异化的实现。
4. 核心概念
(1) Trait 体系总览
graph TB
A[Rust Trait System] --> B[Trait Definition]
A --> C[Implementing Traits]
A --> D[Derivable Traits]
A --> E[Trait as Parameters]
A --> F[Trait Objects]
A --> G[Trait Inheritance]
B --> B1["trait Name { fn method(&self); }"]
C --> C1["impl TraitName for MyType { ... }"]
C --> C2["Default methods in trait"]
D --> D1["#[derive(Debug, Clone, Copy, PartialEq)]"]
D --> D2["Compiler auto-generates implementation"]
E --> E1["fn foo(x: impl Trait)"]
E --> E2["fn foo<T: Trait>(x: T)"]
E --> E3["fn foo<T>(x: T) where T: Trait"]
F --> F1["Box<dyn Trait>"]
F --> F2["Runtime dispatch (vtable)"]
G --> G1["trait A: SuperTrait { }"]
G --> G2["Inherits methods from SuperTrait"]
(2) 静态分发 vs 动态分发
| 特性 | 泛型约束 T: Trait / impl Trait |
Trait 对象 dyn Trait |
|---|---|---|
| 分发时机 | 编译时(静态分发) | 运行时(动态分发) |
| 实现方式 | 单态化——每类型生成独立代码 | 虚表(vtable)——指针间接调用 |
| 性能 | 零开销(可内联) | 有间接调用开销 |
| 二进制体积 | 较大(每类型一份) | 较小(一份代码) |
| 类型要求 | 调用时确定具体类型 | 类型可以不同,只要实现同一 trait |
| 适用场景 | 性能敏感、类型在编译期已知 | 需要异构集合、类型在运行时决定 |
(3) 常用可派生 Trait
| Trait | 作用 | 自动生成的行为 |
|---|---|---|
Debug |
格式化输出 {:?} |
生成调试输出,打印结构体字段名和值 |
Clone |
显式复制 .clone() |
生成 clone 方法,逐个字段复制 |
Copy |
隐式复制(按位拷贝) | 赋值时不转移所有权,而是复制 |
PartialEq |
相等比较 == / != |
生成 eq 方法,逐个字段比较 |
Eq |
完全等价(数学等价关系) | 基于 PartialEq,额外保证自反性 |
Hash |
哈希计算 | 生成 hash 方法,逐个字段计算哈希值 |
Default |
默认值 | 生成 default 方法,每个字段用其默认值 |
(4) Trait 约束组合方式速查
| 约束方式 | 语法 | 适用场景 | 示例 |
|---|---|---|---|
| 内联单约束 | fn foo<T: Trait>(x: T) |
简单单约束 | fn charge<T: UsbCCharge>(d: &T) |
| 内联多约束 | fn foo<T: Trait1 + Trait2>(x: T) |
多个约束 | fn describe<T: Debug + UsbCCharge>(d: &T) |
| where 子句 | fn foo<T>(x: T) where T: Trait |
复杂约束、多类型参数 | where T: UsbCCharge, U: Debug |
| impl Trait | fn foo(x: impl Trait) |
简洁语法糖 | fn plug(d: &impl USBDevice) |
| impl Trait + | fn foo(x: impl Trait1 + Trait2) |
简洁多约束 | fn describe(d: &(impl USBDevice + Debug)) |
5. Trait 示例
▶ 示例 1:Trait 定义与默认方法——设备充电器(难度 ⭐)
// ============================================
// Trait definition with default methods
// ============================================
// Define a trait: any device that can charge via USB-C
trait UsbCCharge {
// Required method: must be implemented
fn charge(&self);
// Default method: implementor MAY override
fn voltage(&self) -> u32 {
18 // Default: standard USB-C 18W
}
// Another default method using self.voltage()
fn charge_time(&self, battery_capacity_mah: u32) -> String {
let hours = battery_capacity_mah as f64 / (self.voltage() as f64 * 1000.0 / 5.0);
format!("{:.1} hours to full charge", hours)
}
}
struct Phone;
struct Laptop;
struct Tablet;
impl UsbCCharge for Phone {
fn charge(&self) {
// Uses the default voltage() -> 18W
println!("Phone: charging at {}W (standard speed)", self.voltage());
}
// voltage() uses default, charge_time() uses default
}
impl UsbCCharge for Laptop {
fn charge(&self) {
println!("Laptop: charging at {}W (fast charging)", self.voltage());
}
fn voltage(&self) -> u32 {
65 // Laptop needs more power
}
}
impl UsbCCharge for Tablet {
fn charge(&self) {
println!("Tablet: charging at {}W (medium speed)", self.voltage());
}
fn voltage(&self) -> u32 {
30
}
}
fn main() {
let phone = Phone;
let laptop = Laptop;
let tablet = Tablet;
phone.charge();
println!(" -> {}", phone.charge_time(3000));
laptop.charge();
println!(" -> {}", laptop.charge_time(6000));
tablet.charge();
println!(" -> {}", tablet.charge_time(5000));
}
输出:
Phone: charging at 18W (standard speed)
-> 0.8 hours to full charge
Laptop: charging at 65W (fast charging)
-> 0.5 hours to full charge
Tablet: charging at 30W (medium speed)
-> 0.8 hours to full charge
UsbCChargetrait 中,charge()是必需方法——每个实现者都必须提供。voltage()和charge_time()是默认方法——提供默认实现,实现者可以选择覆盖也可以直接使用。Phone 只实现了charge(),其余两个方法都用默认实现;Laptop 覆盖了voltage(),其余用默认。
▶ 示例 2:派生 Trait——调试、克隆与比较(难度 ⭐⭐)
// ============================================
// Derivable traits: Debug, Clone, Copy, PartialEq
// ============================================
// Without deriving, none of these operations would work
#[derive(Debug, Clone, Copy, PartialEq)]
struct ChargerSpec {
brand: &'static str,
watts: u32,
usb_c: bool,
}
// PartialEq is needed for this custom type
#[derive(Debug, Clone, PartialEq)]
struct Device {
name: String,
required_watts: u32,
}
fn main() {
// --- Debug: pretty-print with {:?} ---
let spec1 = ChargerSpec { brand: "Anker", watts: 65, usb_c: true };
println!("Debug: {:?}", spec1);
println!("Pretty: {:#?}", spec1);
// --- Clone: explicit copy ---
let spec2 = spec1.clone(); // spec1 is still valid
println!("Cloned: {:?}", spec2);
// --- Copy: implicit copy (only if Copy is derived) ---
let spec3 = spec1; // spec1 is STILL valid because ChargerSpec is Copy!
println!("Copied (implicit): {:?}", spec3);
println!("Original still valid: {:?}", spec1); // Works!
// --- PartialEq: equality comparison ---
let spec_a = ChargerSpec { brand: "Anker", watts: 65, usb_c: true };
let spec_b = ChargerSpec { brand: "Anker", watts: 65, usb_c: true };
let spec_c = ChargerSpec { brand: "Baseus", watts: 65, usb_c: true };
println!("spec_a == spec_b: {}", spec_a == spec_b); // true
println!("spec_a == spec_c: {}", spec_a == spec_c); // false (brand differs)
println!("spec_a != spec_c: {}", spec_a != spec_c); // true
// --- Practical: filtering devices ---
let phone = Device {
name: String::from("Phone"),
required_watts: 18,
};
let laptop = Device {
name: String::from("Laptop"),
required_watts: 65,
};
// Clone a device
let phone_backup = phone.clone();
println!("\nPhone backup: {:?}", phone_backup);
// Compare devices by required_watts
let charger_watts = 65;
let compatible = vec![phone, laptop]
.iter()
.filter(|d| d.required_watts <= charger_watts)
.collect::<Vec<_>>();
println!("Compatible devices (<= {}W): {:?}", charger_watts, compatible);
}
输出:
Debug: ChargerSpec { brand: "Anker", watts: 65, usb_c: true }
Pretty: ChargerSpec {
brand: "Anker",
watts: 65,
usb_c: true,
}
Cloned: ChargerSpec { brand: "Anker", watts: 65, usb_c: true }
Copied (implicit): ChargerSpec { brand: "Anker", watts: 65, usb_c: true }
Original still valid: ChargerSpec { brand: "Anker", watts: 65, usb_c: true }
spec_a == spec_b: true
spec_a == spec_c: false
spec_a != spec_c: true
Phone backup: Device { name: "Phone", required_watts: 18 }
Compatible devices (<= 65W): [Device { name: "Phone", required_watts: 18 }, Device { name: "Laptop", required_watts: 65 }]
用
#[derive(Debug, Clone, Copy, PartialEq)]一行代码,Rust 编译器自动为ChargerSpec生成了四个 trait 的实现。注意Copy和Clone的区别:Clone需要显式调用.clone(),而Copy是隐式按位复制(赋值不会 move)。Device没有Copy是因为String类型没有实现Copy(它在堆上分配内存)。
▶ 示例 3:Trait 作为参数——impl Trait 与泛型约束(难度 ⭐⭐)
// ============================================
// Trait as parameter: impl Trait, generic bounds, where clause
// ============================================
trait USBDevice {
fn device_name(&self) -> &str;
fn power_draw(&self) -> u32;
}
struct Mouse;
struct Keyboard;
struct Webcam;
impl USBDevice for Mouse {
fn device_name(&self) -> &str { "Mouse" }
fn power_draw(&self) -> u32 { 2 }
}
impl USBDevice for Keyboard {
fn device_name(&self) -> &str { "Keyboard" }
fn power_draw(&self) -> u32 { 3 }
}
impl USBDevice for Webcam {
fn device_name(&self) -> &str { "Webcam" }
fn power_draw(&self) -> u32 { 5 }
}
// --- Style 1: impl Trait (sugar for simple cases) ---
fn plug_device(device: &impl USBDevice) {
println!("[Plugged] {} (draws {}W)", device.device_name(), device.power_draw());
}
// --- Style 2: Generic bound T: Trait (explicit type parameter) ---
fn print_device_spec<T: USBDevice>(device: &T) {
println!("[Spec] {} - Power: {}W", device.device_name(), device.power_draw());
}
// --- Style 3: where clause (best for complex bounds) ---
fn check_compatible<T>(device: &T, max_power: u32) -> bool
where
T: USBDevice,
{
device.power_draw() <= max_power
}
// --- Style 4: Multiple trait bounds ---
use std::fmt::Debug;
fn describe_device(device: &(impl USBDevice + Debug)) {
println!("[Debug] Device: {:?}", device);
}
fn main() {
let mouse = Mouse;
let keyboard = Keyboard;
let webcam = Webcam;
// impl Trait syntax
plug_device(&mouse);
plug_device(&keyboard);
// Generic bound syntax
print_device_spec(&webcam);
// where clause
println!("\n--- Compatibility Check (max 3W) ---");
println!("Mouse compatible: {}", check_compatible(&mouse, 3));
println!("Keyboard compatible: {}", check_compatible(&keyboard, 3));
println!("Webcam compatible: {}", check_compatible(&webcam, 3));
// Calculate total power draw for a list
let devices: Vec<&dyn USBDevice> = vec![&mouse, &keyboard, &webcam];
let total_power: u32 = devices.iter().map(|d| d.power_draw()).sum();
println!("\nTotal power draw: {}W / 15W budget", total_power);
}
输出:
[Plugged] Mouse (draws 2W)
[Plugged] Keyboard (draws 3W)
[Spec] Webcam - Power: 5W
--- Compatibility Check (max 3W) ---
Mouse compatible: true
Keyboard compatible: true
Webcam compatible: false
Total power draw: 10W / 15W budget
四种 trait 参数风格各有适用场景:
impl Trait(简洁,适合单一 trait)、T: Trait(明确类型参数名,适合需要引用类型)、where T: Trait(多个约束时可读性最好)、impl Trait + AnotherTrait(多约束。注意Vec<&dyn USBDevice>是 trait 对象(见下一例)——这里用于存储不同类型的引用。
▶ 示例 4:Trait 对象 dyn Trait 与 Trait 继承(难度 ⭐⭐⭐)
// ============================================
// dyn Trait (runtime dispatch) + Trait inheritance
// ============================================
use std::fmt::Debug;
// --- Super trait (trait inheritance) ---
// AnyDevice "inherits" from Debug: to implement AnyDevice,
// a type must also implement Debug
trait AnyDevice: Debug {
fn model_name(&self) -> &str;
}
// UsbDevice extends AnyDevice: it requires Debug + AnyDevice
trait UsbDevice: AnyDevice {
fn usb_version(&self) -> &str;
fn transfer_speed(&self) -> &str;
}
// --- Implement the trait hierarchy ---
#[derive(Debug)]
struct FlashDrive {
name: String,
capacity_gb: u32,
}
impl AnyDevice for FlashDrive {
fn model_name(&self) -> &str {
&self.name
}
}
impl UsbDevice for FlashDrive {
fn usb_version(&self) -> &str {
"USB 3.2 Gen 2"
}
fn transfer_speed(&self) -> &str {
"10 Gbps"
}
}
#[derive(Debug)]
struct ExternalSSD {
name: String,
capacity_tb: f64,
}
impl AnyDevice for ExternalSSD {
fn model_name(&self) -> &str {
&self.name
}
}
impl UsbDevice for ExternalSSD {
fn usb_version(&self) -> &str {
"USB 3.2 Gen 2x2"
}
fn transfer_speed(&self) -> &str {
"20 Gbps"
}
}
// --- Function using trait objects ---
// Accept a heterogeneous collection of UsbDevice implementors
fn list_devices(devices: &[Box<dyn UsbDevice>]) {
for (i, device) in devices.iter().enumerate() {
println!(
"Device #{}: {} ({} - {}, Debug: {:?})",
i + 1,
device.model_name(),
device.usb_version(),
device.transfer_speed(),
device,
);
}
}
// --- Function returning a trait object ---
fn make_device(device_type: &str) -> Option<Box<dyn UsbDevice>> {
match device_type {
"flash" => Some(Box::new(FlashDrive {
name: String::from("SanDisk 128GB"),
capacity_gb: 128,
})),
"ssd" => Some(Box::new(ExternalSSD {
name: String::from("Samsung T7 1TB"),
capacity_tb: 1.0,
})),
_ => None,
}
}
fn main() {
// Heterogeneous collection: different types, same trait
let drive1 = Box::new(FlashDrive {
name: String::from("Kingston 64GB"),
capacity_gb: 64,
});
let drive2 = Box::new(ExternalSSD {
name: String::from("WD My Passport 2TB"),
capacity_tb: 2.0,
});
let all_devices: Vec<Box<dyn UsbDevice>> = vec![drive1, drive2];
println!("--- Connected Devices ---");
list_devices(&all_devices);
// Factory function returning trait objects
println!("\n--- Device Factory ---");
if let Some(device) = make_device("ssd") {
println!("Created: {} ({} - {})", device.model_name(), device.usb_version(), device.transfer_speed());
}
// Trait inheritance in action: UsbDevice requires Debug
// so we can use both {:?} and trait methods
println!("\n--- Debug via Super Trait ---");
let flash = FlashDrive {
name: String::from("Lexar 32GB"),
capacity_gb: 32,
};
// flash has Debug (from AnyDevice: Debug), AnyDevice, and UsbDevice
println!("Debug: {:?}", flash);
println!("Model: {}", flash.model_name());
println!("USB: {}", flash.usb_version());
}
输出:
--- Connected Devices ---
Device #1: Kingston 64GB (USB 3.2 Gen 2 - 10 Gbps, Debug: FlashDrive { name: "Kingston 64GB", capacity_gb: 64 })
Device #2: WD My Passport 2TB (USB 3.2 Gen 2x2 - 20 Gbps, Debug: ExternalSSD { name: "WD My Passport 2TB", capacity_tb: 2.0 })
--- Device Factory ---
Created: Samsung T7 1TB (USB 3.2 Gen 2x2 - 20 Gbps)
--- Debug via Super Trait ---
Debug: FlashDrive { name: "Lexar 32GB", capacity_gb: 32 }
Model: Lexar 32GB
USB: USB 3.2 Gen 2
Trait 继承(超 trait):
trait AnyDevice: Debug意味着"任何实现AnyDevice的类型必须也实现Debug"。trait UsbDevice: AnyDevice进一步叠加——形成一个三层 trait 层次。实现UsbDevice的类型必须同时实现Debug+AnyDevice+UsbDevice的所有方法。Trait 对象
dyn Trait:使用Box<dyn UsbDevice>可以在一个集合中存储不同类型但实现了同一 trait 的对象。方法调用通过虚表(vtable)在运行时派发——有微小性能开销,但换取极大的灵活性。make_device函数返回Option<Box<dyn UsbDevice>>体现了"工厂模式"。
▶ 示例 5:综合练习——图形面积计算(难度 ⭐⭐⭐)
// ============================================
// 综合示例:Trait + 泛型约束 + dyn Trait
// ============================================
use std::fmt::Debug;
trait Shape: Debug {
fn area(&self) -> f64;
fn name(&self) -> &str;
fn describe(&self) -> String {
format!("{}: 面积 = {:.2}", self.name(), self.area())
}
}
#[derive(Debug)]
struct Circle { radius: f64 }
#[derive(Debug)]
struct Rectangle { width: f64, height: f64 }
#[derive(Debug)]
struct Triangle { base: f64, height: f64 }
impl Shape for Circle {
fn area(&self) -> f64 { std::f64::consts::PI * self.radius * self.radius }
fn name(&self) -> &str { "圆形" }
}
impl Shape for Rectangle {
fn area(&self) -> f64 { self.width * self.height }
fn name(&self) -> &str { "矩形" }
}
impl Shape for Triangle {
fn area(&self) -> f64 { 0.5 * self.base * self.height }
fn name(&self) -> &str { "三角形" }
}
fn total_area(shapes: &[Box<dyn Shape>]) -> f64 {
shapes.iter().map(|s| s.area()).sum()
}
fn largest<T: Shape>(shapes: &[T]) -> &T {
shapes.iter().max_by(|a, b| a.area().partial_cmp(&b.area()).unwrap()).unwrap()
}
fn print_all(shapes: &[Box<dyn Shape>]) {
for s in shapes {
println!(" {}", s.describe());
}
}
fn main() {
let shapes_static: Vec<&dyn Shape> = vec![
&Circle { radius: 5.0 },
&Rectangle { width: 4.0, height: 6.0 },
&Triangle { base: 3.0, height: 8.0 },
];
println!("=== 静态引用遍历 ===");
for s in &shapes_static {
println!(" {}", s.describe());
}
let shapes_dynamic: Vec<Box<dyn Shape>> = vec![
Box::new(Circle { radius: 10.0 }),
Box::new(Rectangle { width: 3.0, height: 7.0 }),
Box::new(Triangle { base: 6.0, height: 4.0 }),
];
println!("\n=== 动态分发 ===");
print_all(&shapes_dynamic);
println!("总面积: {:.2}", total_area(&shapes_dynamic));
let homogenous = vec![
Circle { radius: 3.0 },
Circle { radius: 7.0 },
Circle { radius: 5.0 },
];
let biggest = largest(&homogenous);
println!("\n最大圆形: {}", biggest.describe());
}
输出:
=== 静态引用遍历 ===
圆形: 面积 = 78.54
矩形: 面积 = 24.00
三角形: 面积 = 12.00
=== 动态分发 ===
圆形: 面积 = 314.16
矩形: 面积 = 21.00
三角形: 面积 = 12.00
总面积: 347.16
最大圆形: 圆形: 面积 = 153.94
同一个
Shapetrait 被三种方式使用:&dyn Shape静态引用切片、Box<dyn Shape>动态分发集合、泛型约束T: Shape找最大值。describe是 trait 的默认方法,所有实现者自动继承。
❓ 常见问题
impl Trait 和 dyn Trait 有什么区别?什么时候用哪个?impl Trait 是编译时静态分发,dyn Trait 是运行时动态分发。#[derive(Debug)] 和手动实现有什么区别?Copy 和 Clone 的区别到底是什么?Copy 是隐式按位复制(赋值不转移所有权),Clone 是显式深拷贝(调用 .clone())。T: Trait1 + Trait2 和 where T: Trait1 + Trait2 写法一样吗?📖 小节
- trait 定义
trait Name { fn method(&self); }声明了一组行为契约,impl 块为具体类型实现这些契约 - 默认方法在 trait 中提供默认实现,实现者可以选择覆盖(override)也可以直接沿用
- 派生 trait
#[derive(Debug, Clone, Copy, PartialEq)]让编译器自动生成常见 trait 实现——零成本抽象 impl Trait(静态分发)和dyn Trait(动态分发)是两种 trait 使用方式——前者零开销,后者更灵活- trait 继承
trait A: SuperTrait建立 trait 层次——实现者必须同时满足所有超 trait 的约束 - trait 是 Rust 实现零成本抽象的核心机制——没有虚函数开销的"接口"(泛型),也有需要时可选运行时多态(trait 对象)
📝 作业
-
难度 ⭐:定义一个
trait Drawable { fn draw(&self); }。为两个结构体Circle和Square实现该 trait,各打印不同的图形。写一个泛型函数fn render<T: Drawable>(item: &T)调用draw。在 main 中分别渲染一个圆形和一个正方形。 -
难度 ⭐⭐:定义一个
trait Summary { fn summarize(&self) -> String; fn author(&self) -> &str; },其中author()为默认方法,返回"Anonymous"。为结构体Article { title: String, content: String }和Tweet { username: String, text: String }实现该 trait。创建两个文章和两条推文,放入Vec<Box<dyn Summary>>中遍历打印摘要。 -
难度 ⭐⭐⭐:定义一个 trait 层次:
trait Vehicle: std::fmt::Debug { fn fuel_type(&self) -> &str; },然后trait ElectricVehicle: Vehicle { fn battery_capacity_kwh(&self) -> f64; fn range_km(&self) -> f64; }。为结构体TeslaModel3和NissanLeaf实现ElectricVehicle。写一个函数fn print_fleet(vehicles: &[Box<dyn ElectricVehicle>])遍历并打印每辆车的信息。在 main 中创建两种车的实例并测试。