注:本篇文章全是由我本人通过学习所感悟的内容,所以不能保证是绝对正确的,如果有发现我文章的错误,麻烦请指出我的错误让我能明白我自己错在了哪里,希望大家一起进步。
trait
trait是什么?我先举个例子,在RUST中有非常多的数据类型,i32、u32、string、结构体和枚举等数据类型,而有的数据类型可以直接使用println!("{}", 变量);输出,我们前面了解到只有实现了display这个trait的数据类型才能被输出,如果没有则不行,你发现没这里面主要涉及到的就是数据类型,而trait本质就是为不同的数据类型实现相同的抽象功能。下面我们开始实战举例。下面是一个实例,我们定义了三个结构体及一个trait,而观察程序我们为bob和tom实现了一个attack的trait,这是两个类型,而这两个类型都有一个共同attack,这就是trait在不同的类型实现一个相同的功能,这个功能可以自定义,但是一定要满足trait中的签名,而我们直接在trait中定义的则是默认实现,就像tom中我们并没写具体的内容,而是使用的默认的trait中的内容。下面来开始正式内容把。
rust
trait attack {
fn can_attack(&self) {
println!("我能攻击");
}
}
impl attack for Bob {
fn can_attack(&self) {
println!("Bob能攻击");
}
}
impl attack for Tom {
}
struct Bob {
name: String,
attack: bool,
}
struct Tom {
name: String,
attack: bool,
}
struct Xiaohong {
name: String,
attack: bool,
}
fn main() {
let Bob = Bob {
name: "Bob".to_string(),
attack: true,
};
let Tom = Tom {
name: "Tom".to_string(),
attack: true,
};
let Xiaohong = Xiaohong {
name: "Tom".to_string(),
attack: false,
};
Bob.can_attack();
Tom.can_attack();
}
//输出
Bob能攻击
我能攻击
为类型实现trait
前面说了,trait是为类型服务的,所以我们下面来拆分上面的代码演示如何为类型实现一个trait。看这个例子就可以发现,实现trait的方式其实很简单,就是impl trait的名字 for 类型的名字 {}然后在里面定义trait的方法就可以实现了,也可以在定义trait的时候直接书写一个完整的函数这样它可以默认使用。
rust
//这里我们定义了一个结构体,也就是一个类型。
struct Bob {
name: String,
attack: bool,
}
//这里我们定义了一个trait并实现了can_attack方法。
trait attack {
fn can_attack(&self) {
println!("我能攻击");
}
}
//这里我们为Bob类型实现了trait。
impl attack for Bob {
fn can_attack(&self) {
println!("Bob能攻击");
}
}
孤儿原则
孤儿原则是什么,要为一个类型实现一个 trait,这个 trait 或这个类型必须至少有一个是在当前 crate 中定义的。为什么要这样写呢?如果两个都不在当前crate可能存在编译器并不知道你要使用那个trait或那个类型。
默认实现
以《为类型实现trait》的例子举例,当我们在trait中的函数默认定义了内容,这就是默认实现。
rust
//这里我们定义了一个trait并实现了can_attack方法。
trait attack {
fn can_attack(&self) {
println!("我能攻击");
}
}
trait约束
trait约束,什么叫trait约束呢,在函数的参数列表或返回值中,我们想定义一个参数,而我们想这个参数是实现了某个或多个trait的,这就叫trait约束。
trait作为参数
下面是一个很简单的例子,当我们想传递的参数是实现了attack的类型,我们可以直接使用impl trait名,来指定。
rust
fn iambob(me: &impl attack) {
println!("I am Bob!!!");
}
+号约束
+号可以在我们想指定一个参数同时实现了多种trait的情况下使用,下面我们有定义了一个fly trait,而我们想传的参数同时满足了attack和fly,我们就可以这样表示(impl trait1 + trait2),举一反三哈可以多+几个。
rust
trait attack {
fn can_attack(&self) {
println!("我能攻击");
}
}
trait fly {
fn can_fly(&self) {
println!("我会飞哦");
}
}
impl fly for Bob {}
impl attack for Bob {
fn can_attack(&self) {
println!("Bob能攻击");
}
}
fn iambob(me: &(impl attack + fly)) {
println!("I am Bob!!!");
}
泛型约束
下面是另一个例子,用于泛型来约束参数,其实看一眼明白了<T: trait1 + trait2>,相反可以只是先一个或更多,自己举一反三把。
rust
trait attack {
fn can_attack(&self) {
println!("我能攻击");
}
}
trait fly {
fn can_fly(&self) {
println!("我会飞哦");
}
}
impl fly for Bob {}
impl attack for Bob {
fn can_attack(&self) {
println!("Bob能攻击");
}
}
fn iambob<T: attack + fly>(me: T) {
println!("I am Bob!!!");
}
where简写
where是为了在约束很多trait的情况下让格式更美观。直接看实例吧,泛型T实现了attack+fly,语法就是在定义返回值之后的位置
where
T: trait1 + trait2,
其他泛型:其他trait,
rust
fn iambob<T>(me: &T)
where
T: attack + fly,
{
println!("I am Bob!!!");
}