在面向对象编程语言,例如 Java,要实现一些相同行为的场景,就需要通过 interface 来实现,rust 也有类似 interface 的特性,就叫做 traits 。简单来说,traits 是定义共享行为的方式。
简单例子
定义一个 traits 以 trait 开头,后面跟着特质的名称,然后在大括号中定义方法签名,方法签名是一个方法的名称、参数列表和返回值类型,但不包括方法体。
我们来定一个 Summary trait,它有一个 summarize
方法,返回一个字符串:
// src/lib.rspub trait Summary { fn summarize(&self) -> String;}
接下来我们实现这个特质:
// src/lib.rspub 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.rsuse 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 可以限制泛型的类型,使泛型只能是某个特质的类型。