在面向对象编程语言,例如 Java,要实现一些相同行为的场景,就需要通过 interface 来实现,rust 也有类似 interface 的特性,就叫做 traits 。简单来说,traits 是定义共享行为的方式

简单例子

定义一个 traits 以 trait 开头,后面跟着特质的名称,然后在大括号中定义方法签名,方法签名是一个方法的名称、参数列表和返回值类型,但不包括方法体。

我们来定一个 Summary trait,它有一个 summarize 方法,返回一个字符串:

// src/lib.rs
pub trait Summary {
fn summarize(&self) -> String;
}

接下来我们实现这个特质:

// src/lib.rs
pub struct Tweet {
pub username: String,
pub content: String,
}
// 为 Tweet 结构体实现 Summary 特质
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}

最后可以这样调用

// src/main.rs
use hello_world::{Summary, Tweet};
fn main() {
let tweet = Tweet {
username: String::from("kelen"),
content: String::from(
"kelen.cc is a blog about programming, software and technology.",
),
};
println!("1 new tweet: {}", tweet.summarize());
}

在 main.rs 中使用 Tweet 必须要引入 Summary 特质,这样就可以调用 summarize 方法。这是因为 rust 不会自动将所有可能的方法都引入作用域,所以必须要显式引入。如果不将 Summary 引入作用域,编译器就无法找到 summarize 方法。

通过这个简单例子,可以看到 traits 的使用方法,它可以定义一些共享的行为,然后由具体的类型去实现这些行为,统一不同类型的行为。

默认实现

traits 可以定义默认实现,这样当某个类型没有实现某个方法时,可以调用默认实现。

pub trait Drawable {
fn draw(&self) {
println!("绘制一个默认图形。");
}
}
pub struct Circle {
pub radius: f64,
}
impl Drawable for Circle {} // 不实现 draw 方法
fn main() {
let c = Circle { radius: 1.0 };
c.draw(); // print:绘制一个默认图形。
}

在上面的例子中,Circle 结构体没有实现 draw 方法,但是由于 Drawable 特质有默认实现,所以可以调用 draw 方法。

Trait Bound

Trait Bound(特质边界)是一种约束,它可以指定泛型是某个特质的类型。在泛型定义中使用 Trait Bound 语法,可以限制泛型的类型。

// 泛型函数 print_area,带有 Shape 特质边界
fn print_area<T: Shape>(shape: T) {
println!("面积是: {}", shape.area());
}
fn main() {
// 创建 Rectangle 和 Circle 实例
let rectangle = Rectangle { width: 5.0, height: 3.0 };
let circle = Circle { radius: 2.0 };
// 调用泛型函数 print_area
print_area(rectangle);
print_area(circle);
}

使用 where 子句

如果有多个 Trait Bound,可以使用 where 子句来简化代码。

fn print_area<T>(shape: T) where T: Shape {
println!("面积是: {}", shape.area());
}

where 子句可以简化代码,但是不能简化泛型函数的签名。

返回实现了特质的类型

有时候,我们需要返回实现了特质的类型,可以使用 impl Trait 语法。

// 定义 Shape 特质
trait Shape {
fn area(&self) -> f64;
}
// 定义 Rectangle 结构体
struct Rectangle {
width: f64,
height: f64,
}
// 为 Rectangle 实现 Shape 特质
impl Shape for Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}
// 返回实现了 Shape 特质的具体类型
fn create_shape() -> impl Shape {
Rectangle { width: 5.0, height: 3.0 }
}
fn main() {
let shape = create_shape();
println!("面积是: {}", shape.area());
}

总结

traits 是一种定义共享行为的方式,可以为不同的类型实现相同的行为。traits 可以定义默认实现,当某个类型没有实现某个方法时,可以调用默认实现。Trait Bound 可以限制泛型的类型,使泛型只能是某个特质的类型。