trait可以暂时先翻译为“特征”,也可以不翻译吧,就叫trait。
//定义trait
pub trait GetInfo {
fn get_name(&self) -> &String;
fn get_index(&self) -> i32;
}
//定义学生结构体
pub struct Student {
pub name : String,
pub index : i32,
Is_Homework_completed : bool
}
pub struct Teacher {
pub name : String,
pub index : i32,
pub sex : String
}
//实现trait
impl GetInfo for Student {
fn get_name(&self) -> &String {
&self.name
}
fn get_index(&self) -> i32 {
self.index
}
}
impl GetInfo for Teacher {
fn get_name(&self) -> &String {
&self.name
}
fn get_index(&self) -> i32 {
self.index
}
}
fn main() {
let stu = Student { name: String::from("二狗"), index: 32 , Is_Homework_completed : false};
println!("stu: {}, {}", stu.get_name(), stu.get_index());
let t = Teacher { name: String::from("小芳"), index: 5 , sex : String::from("male")};
println!("t: {}, {}", t.get_name(), t.get_index());
}
运行结果:
stu: 二狗, 32
t: 小芳, 5
在上面例子中,我们定义了一个trait,并分别为Student和Teacher实现了这个trait。
可是这个例子好像并不能体现trait的作用,我们本来就可以给结构体写这些函数呀。那么trait有什么用处呢?下面举一个例子。
//定义trait
pub trait GetInfo {
fn get_name(&self) -> &String;
fn get_index(&self) -> i32;
}
//定义学生结构体
pub struct Student {
pub name : String,
pub index : i32,
Is_Homework_completed : bool
}
pub struct Teacher {
pub name : String,
pub index : i32,
pub sex : String
}
//实现trait
impl GetInfo for Student {
fn get_name(&self) -> &String {
&self.name
}
fn get_index(&self) -> i32 {
self.index
}
}
//此处我们把Teacher的实现注视掉
//impl GetInfo for Teacher {
// fn get_name(&self) -> &String {
// &self.name
// }
//
// fn get_index(&self) -> i32 {
// self.index
// }
//}
fn Print_info(item : impl GetInfo) {
println!("name: {}", item.get_name());
println!("index: {}", item.get_index());
}
fn main() {
let stu = Student { name: String::from("二狗"), index: 32 , Is_Homework_completed : false};
Print_info(stu);
}
运行结果:
name: 二狗
index: 32
此时trait的作用就体现出来了。Print_info函数要求:只有实现了GetInfo的数据结构才能调用Print_info函数。作为一种约束。
上面是通过impl trait进行约束。
还可以通过trait bound来进行约束。
什么是trait bound呢,在上一期泛型中的介绍的
fn find_max (list : &[T]) -> T {
let mut max = list[0];
for &i in list.iter() {
if i > max {
max = i;
}
}
max
}
这里的
pub fn haha(item1: impl trait1, item2: impl trait2) {
这里的item1和item2是两个不同的类型,那么如果我需要是相同的类型呢,那就只能trait bound才能做到。
fn find_max (list : &[T]) -> T {
trait是可以有默认实现的。
举一个简单的例子:
//定义学生结构体
pub struct Student {
pub name : String,
pub index : i32,
Is_Homework_completed : bool
}
pub struct Teacher {
pub name : String,
pub index : i32,
pub sex : String
}
trait Print_school {
fn print_shoolname(&self) {
println!("你没有为这个类型实现这个trait!");
}
}
impl Print_school for Student {
}
impl Print_school for Teacher {
fn print_shoolname(&self) {
println!("老师的学校是电子科大");
}
}
fn main() {
let stu = Student { name: String::from("二狗"), index: 32 , Is_Homework_completed : false};
let tea = Teacher {name : "白质".to_string(), index : 8, sex : String::from("男")};
stu.print_shoolname();
tea.print_shoolname();
}
运行结果:
你没有为这个类型实现这个trait!
老师的学校是电子科大
这个例子应该是很好理解的。不需要解释吧。
这个先埋伏笔。以后在讲迭代器和闭包时如果我还能记得起的话,再举例讲解。
我们还可以通过trait为其他的类型添加成员方法,哪怕这个类型不是我们自己写的。
例如我们对i32添加一个方法:
trait Triple {
fn triple(&self) -> i32;
}
impl Triple for i32 {
fn triple(&self) -> i32 {
*self * 3
}
}
fn main() {
let a = 10;
println!("res: {}", a.triple());
}
运行结果:
res: 30
但是这并不是完全无条件的。Rust规定:impl块要么与trait的声明在同一个crate中,要么与类型的声明在同一个crate中。其实就是说,至少有一个在当前crate中。
前面我们在讲Copy和Clone时就看到过这个单词。
我们在impl某些trait时,其实是很机械化的。然而为很多类型重复单调的实现impl某些trait,非常滴不爽。因此,Rust提供了这个Derive,能够帮我们自动实现impl某些trait。
包括以下:
Debug Clone Copy Hash RustcEncodable RustcDecodable PartialEq Eq ParialOrd Ord Default FromPrimitive Send Sync
语法格式:
#[derive(Copy)]
后面我们可能会出一期,讲一讲常见的Derive应用场景。
欢迎关注微信公众号:Rust编程之路