Trait 系统
Trait 是 Fleet 语言的核心特性之一,提供了强大的抽象和多态能力。本章将详细介绍如何定义和使用 trait。
🎯 什么是 Trait
Trait 定义了一组方法签名,类似于其他语言中的接口。任何类型都可以实现 trait,从而获得这些方法的能力。
// 定义一个 trait
trait Display {
fn show(self) -> str;
fn format(self, prefix: str) -> str;
}
📝 定义 Trait
基本 Trait 定义
trait Drawable {
fn draw(self);
fn area(self) -> f64;
}
trait Comparable {
fn compare(self, other: Self) -> int;
}
带返回类型的方法
trait Calculator {
fn add(self, other: int) -> int;
fn multiply(self, factor: f64) -> f64;
fn to_string(self) -> str;
}
🏗️ 实现 Trait
使用 impl 关键字为类型实现 trait:
为结构体实现 Trait
struct Point {
x: int,
y: int,
}
struct Circle {
radius: f64,
}
impl Display for Point {
fn show(self) -> str {
return "Point";
}
fn format(self, prefix: str) -> str {
return prefix + "Point(" + self.x + ", " + self.y + ")";
}
}
impl Display for Circle {
fn show(self) -> str {
return "Circle";
}
fn format(self, prefix: str) -> str {
return prefix + "Circle(radius: " + self.radius + ")";
}
}
Self 参数
self 参数代表当前实例,支持字段访问:
struct Rectangle {
width: f64,
height: f64,
}
impl Drawable for Rectangle {
fn draw(self) {
print("Drawing rectangle: " + self.width + "x" + self.height);
}
fn area(self) -> f64 {
return self.width * self.height;
}
}
fn main() {
let rect = Rectangle { width: 10.0, height: 5.0 };
rect.draw();
let area = rect.area();
print("Area: " + area);
}
🔄 多 Trait 实现
一个类型可以实现多个 trait:
trait Debug {
fn debug(self) -> str;
}
trait Clone {
fn clone(self) -> Self;
}
struct Person {
name: str,
age: int,
}
impl Display for Person {
fn show(self) -> str {
return self.name;
}
fn format(self, prefix: str) -> str {
return prefix + self.name + " (" + self.age + " years old)";
}
}
impl Debug for Person {
fn debug(self) -> str {
return "Person { name: \"" + self.name + "\", age: " + self.age + " }";
}
}
fn main() {
let person = Person { name: "Alice", age: 30 };
// 使用不同 trait 的方法
print(person.show());
print(person.format(">>> "));
print(person.debug());
}
🎨 实际应用示例
形状系统
trait Shape {
fn area(self) -> f64;
fn perimeter(self) -> f64;
fn describe(self) -> str;
}
struct Rectangle {
width: f64,
height: f64,
}
struct Circle {
radius: f64,
}
impl Shape for Rectangle {
fn area(self) -> f64 {
return self.width * self.height;
}
fn perimeter(self) -> f64 {
return 2.0 * (self.width + self.height);
}
fn describe(self) -> str {
return "Rectangle: " + self.width + "×" + self.height;
}
}
impl Shape for Circle {
fn area(self) -> f64 {
return 3.14159 * self.radius * self.radius;
}
fn perimeter(self) -> f64 {
return 2.0 * 3.14159 * self.radius;
}
fn describe(self) -> str {
return "Circle: radius " + self.radius;
}
}
fn print_shape_info(shape: impl Shape) {
print(shape.describe());
print("Area: " + shape.area());
print("Perimeter: " + shape.perimeter());
}
fn main() {
let rect = Rectangle { width: 5.0, height: 3.0 };
let circle = Circle { radius: 2.0 };
print_shape_info(rect);
print("---");
print_shape_info(circle);
}
序列化系统
trait Serializable {
fn serialize(self) -> str;
fn content_type(self) -> str;
}
struct User {
id: int,
name: str,
email: str,
}
struct Product {
sku: str,
name: str,
price: f64,
}
impl Serializable for User {
fn serialize(self) -> str {
return "{\"id\":" + self.id + ",\"name\":\"" + self.name + "\",\"email\":\"" + self.email + "\"}";
}
fn content_type(self) -> str {
return "application/json";
}
}
impl Serializable for Product {
fn serialize(self) -> str {
return "{\"sku\":\"" + self.sku + "\",\"name\":\"" + self.name + "\",\"price\":" + self.price + "}";
}
fn content_type(self) -> str {
return "application/json";
}
}
fn export_data(item: impl Serializable) {
print("Content-Type: " + item.content_type());
print("Data: " + item.serialize());
}
fn main() {
let user = User { id: 1, name: "Alice", email: "alice@example.com" };
let product = Product { sku: "ABC123", name: "Laptop", price: 999.99 };
export_data(user);
print("---");
export_data(product);
}
🔍 Trait 方法调用
直接调用
fn main() {
let rect = Rectangle { width: 4.0, height: 3.0 };
// 直接调用 trait 方法
let area = rect.area();
let desc = rect.describe();
print(desc + " has area " + area);
}
方法链调用
trait Chainable {
fn step1(self) -> Self;
fn step2(self) -> Self;
fn finalize(self) -> str;
}
struct Builder {
value: str,
}
impl Chainable for Builder {
fn step1(self) -> Self {
return Builder { value: self.value + "->step1" };
}
fn step2(self) -> Self {
return Builder { value: self.value + "->step2" };
}
fn finalize(self) -> str {
return self.value + "->done";
}
}
fn main() {
let builder = Builder { value: "start" };
let result = builder.step1().step2().finalize();
print(result); // 输出: start->step1->step2->done
}
⚠️ 错误检测
Fleet 的类型检查器会验证 trait 实现的正确性:
缺少方法
trait Complete {
fn method1(self) -> str;
fn method2(self) -> int;
}
struct Incomplete {
value: str,
}
// 错误:缺少 method2
impl Complete for Incomplete {
fn method1(self) -> str {
return self.value;
}
// 编译错误:impl Complete for Incomplete 缺少必需方法: method2
}
方法签名不匹配
trait Signature {
fn process(self, input: str) -> int;
}
struct Wrong {
data: str,
}
// 错误:方法签名不匹配
impl Signature for Wrong {
fn process(self, input: int) -> str { // 参数和返回类型都错误
return "wrong";
}
// 编译错误:方法签名不匹配
}
🎯 最佳实践
1. 单一职责原则
// 好的设计:每个 trait 职责单一
trait Readable {
fn read(self) -> str;
}
trait Writable {
fn write(self, data: str);
}
// 而不是混合职责
trait BadFileHandler {
fn read(self) -> str;
fn write(self, data: str);
fn compress(self);
fn encrypt(self);
}
2. 合理的方法命名
trait WellNamed {
fn calculate_area(self) -> f64; // 清晰的动词+名词
fn is_valid(self) -> bool; // 布尔方法用 is_ 前缀
fn to_string(self) -> str; // 转换方法用 to_ 前缀
}
3. 适当的抽象级别
// 好的抽象:通用且有用
trait Drawable {
fn draw(self);
fn bounds(self) -> Rectangle;
}
// 避免过度具体的抽象
trait TooSpecific {
fn draw_with_red_pen_on_white_paper(self); // 太具体
}
🎯 练习
尝试实现以下 trait 系统:
- 媒体播放器:定义
Playabletrait,为Audio和Video结构体实现 - 数据存储:定义
Storabletrait,为不同的存储后端实现 - 数学运算:定义
Numerictrait,为数字类型实现基本运算
示例解答
// 1. 媒体播放器
trait Playable {
fn play(self);
fn pause(self);
fn duration(self) -> int;
}
struct Audio {
filename: str,
length: int,
}
struct Video {
filename: str,
length: int,
resolution: str,
}
impl Playable for Audio {
fn play(self) {
print("Playing audio: " + self.filename);
}
fn pause(self) {
print("Pausing audio: " + self.filename);
}
fn duration(self) -> int {
return self.length;
}
}
impl Playable for Video {
fn play(self) {
print("Playing video: " + self.filename + " (" + self.resolution + ")");
}
fn pause(self) {
print("Pausing video: " + self.filename);
}
fn duration(self) -> int {
return self.length;
}
}
fn main() {
let song = Audio { filename: "song.mp3", length: 180 };
let movie = Video { filename: "movie.mp4", length: 7200, resolution: "1080p" };
song.play();
movie.play();
print("Song duration: " + song.duration() + " seconds");
print("Movie duration: " + movie.duration() + " seconds");
}
📖 下一步
恭喜你掌握了 Fleet 的 trait 系统!这是 Fleet 最强大的特性之一。接下来,让我们学习 模式匹配,了解如何优雅地处理不同的数据情况。