在rust中Trait 约束用于指定一个泛型类型必须实现的特定 Trait。Trait 约束允许你在泛型类型参数列表中指定类型必须满足的条件,这样可以在编译时确保类型具有某些行为 下面我们将从
- trait约束语法
- 多个trait约束
- 生命周期约束
- 默认trait实现
- 高级trait约束
- 函数返回中的 impl Trait
trait约束语法
fn generic_function<T: Trait>(arg: T) {
// 在这里可以使用 Trait 中的方法
}
在这个例子中,T 是一个泛型类型参数,: Trait 表示 T 必须实现 Trait。下面我们给出一个具体的例子来具体展示怎么使用trait的约束怎么使用:
trait Printable {
fn print(&self);
}
struct Point {
x: i32,
y: i32,
}
impl Printable for Point {
fn print(&self) {
println!("Point({}, {})", self.x, self.y);
}
}
fn print_if_printable<T: Printable>(item: T) {
item.print();
}
fn main() {
let p = Point { x: 1, y: 2 };
print_if_printable(p);
}
在这个例子中,Printable 是一个 Trait,它有一个 print 方法。Point 结构体实现了 Printable Trait。print_if_printable 函数接受任何实现了 Printable 的类型 T,并调用它的 print 方法。
多个 Trait 约束
我们可以为一个泛型指定多个trait,语法如下:
fn generic_function<T: Trait1 + Trait2>(arg: T) {
// 在这里可以使用 Trait1 和 Trait2 中的方法
}
这里 T 必须同时实现 Trait1 和 Trait2,例如:
trait Drawable {
fn draw(&self);
}
trait Erasable {
fn erase(&self);
}
struct Shape {
color: String,
}
impl Drawable for Shape {
fn draw(&self) {
println!("Drawing shape with color {}", self.color);
}
}
impl Erasable for Shape {
fn erase(&self) {
println!("Erasing shape with color {}", self.color);
}
}
fn process<T: Drawable + Erasable>(item: T) {
item.draw();
item.erase();
}
fn main() {
let shape = Shape { color: "red".to_string() };
process(shape);
}
在这个例子中,Shape 结构体实现了 Drawable 和 Erasable 两个 Trait。process 函数接受任何同时实现了 Drawable 和 Erasable 的类型 T,并依次调用它的 draw 和 erase 方法。
生命周期约束
除了 Trait 约束,你还可以为泛型类型指定生命周期约束:
fn generic_function<'a, T>(arg: &'a T) {
// 这里 'a 是一个生命周期参数,T 必须至少拥有 'a 生命周期
}
下面我们给出生命周期约束的具体例子:
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("long string is long");
let result;
{
let string2 = String::from("xyz");
result = longest(string1.as_str(), string2.as_str());
}
println!("The longest string is: {}", result);
}
在这个例子中,longest 函数接受两个字符串切片引用,并返回它们中较长的一个。这里的 'a 是一个生命周期参数,表示两个输入字符串切片必须至少拥有 'a 生命周期。
默认 Trait 实现
如果一个类型已经为某个 Trait 提供了默认实现,你可以在泛型函数中直接使用该实现:
trait Runable{
fn run(&self){
println!("任何动物都会跑");
}
}
struct Animal{
name:String,
}
impl Runable for Animal{
}
main(){
let animal = Animal{
name:String::from("dog"),
};
animal.run();
}
我们首先定义一个Runable的trait,里面有一个默认实现的方法,打印出来“任何动物都会跑”。然后定义一个结构体Animal用来实现Runable trait,然后在main方法中创建animal的Animal类型的变量,然后animal.run()用来调用Runable的trait的默认实现方法。
高级 Trait 约束
Rust 还支持高级的 Trait 约束,如:
- ?Sized:允许泛型类型是大小未知的类型(比如 trait 对象)。
- 高级生命周期约束:如 'static,表示类型拥有 'static 生命周期。
函数返回中的 impl Trait
/// 定义一个名为Summary的trait
trait Summary{
fn summary(&self)->String;
}
fn returns_summarizable() -> impl Summary {
Weibo {
username: String::from("sunface"),
content: String::from(
"m1 max太厉害了,电脑再也不会卡",
)
}
}
因为 Weibo 实现了 Summary,因此这里可以用它来作为返回值。要注意的是,虽然我们知道这里是一个 Weibo 类型,但是对于 returns_summarizable 的调用者而言,他只知道返回了一个实现了 Summary 特征的对象,但是并不知道返回了一个 Weibo 类型。
这种 impl Trait 形式的返回值,在一种场景下非常非常有用,那就是返回的真实类型非常复杂,你不知道该怎么声明时(毕竟 Rust 要求你必须标出所有的类型),此时就可以用 impl Trait 的方式简单返回。例如,闭包和迭代器就是很复杂,只有编译器才知道那玩意的真实类型,如果让你写出来它们的具体类型,估计内心有一万只草泥马奔腾,好在你可以用 impl Iterator 来告诉调用者,返回了一个迭代器,因为所有迭代器都会实现 Iterator 特征
本文使用 markdown.com.cn 排版