/*
特性(trait)概念接近于 Java 中的接口(Interface),但两者不完全相同。特性与接口相同的地方在于它们都是一种行为规范,可以用于标识哪些类有哪些方法
trait Descriptive {
fn describe(&self) -> String;
}
Descriptive 规定了实现者必须有是 describe(&self) -> String 方法。(必须实现所有方法)
几个非常有用的标准库特性:
Drop提供了当一个值退出作用域后执行代码的功能,它只有一个drop(&mut self)方法。
Borrow用于创建一个数据结构时把拥有和借用的值看作等同。
AsRef用于在泛型中把一个值转换为引用。
Deref<Target=T>用于把&U类型的值自动转换为&T类型。
Iterator用于在集合 (collection) 和惰性值生成器 (lazy value generator) 上实现迭代器。
Sized用于标记运行时长度固定的类型,而不定长的切片和特性必须放在指针后面使其运行时长度已知, 比如&[T]和Box<Trait>。
*/
//特性简单使用
fn test_1() {
trait Descriptive {
fn describe(&self) -> String;
}
struct Person {
name: String,
age: u8,
}
impl Descriptive for Person {
fn describe(&self) -> String {
format!("{} {}", self.name, self.age)
}
}
}
//特性不仅可以绑定结构体方法,还可以绑定结构体关联方法
fn test_1_2() {
trait Descriptive {
fn describe() -> String;
}
struct Person {
name: String,
age: u8,
}
impl Descriptive for Person {
fn describe() -> String {
//format!("{} {}", self.name, self.age)
String::from("1231111111111111111111")
}
}
fn test2_1(t:impl Descriptive){
println!("{}",Person::describe());
}
let p =Person{name: String::from("123"),age: 12,};
test2_1(p);
}
//默认特性
fn test_2() {
//这是特性与接口的不同点:接口只能规范方法而不能定义方法,但特性可以定义方法作为默认方法,因为是"默认",
//所以对象既可以重新定义方法,也可以不重新定义方法使用默认的方法
trait Descriptive {
fn describe(&self) -> String {
String::from("[Object]")
}
}
struct Person {
name: String,
age: u8,
}
impl Descriptive for Person {
//如果想调用特性的默认接口,把下面这段代码去掉就可以了
fn describe(&self) -> String {
format!("{} {}", self.name, self.age)
}
}
let cali = Person {
name: String::from("Cali"),
age: 24,
};
println!("{}", cali.describe());
}
//特性做参数
fn test_3() {
trait Descriptive {
fn describe(&self) -> String {
String::from("[Object]")
}
}
//任何实现了 Descriptive 特性的对象都可以作为这个函数的参数,这个函数没必要了解传入对象有没有其他属性或方法,
//只需要了解它一定有 Descriptive 特性规范的方法就可以了。当然,此函数内也无法使用其他的属性与方法。
fn output(object: impl Descriptive) {
println!("{}", object.describe());
}
//特性参数还可以用这种等效语法实现:
fn output2<T: Descriptive>(object: T) {
println!("{}", object.describe());
}
//这是一种风格类似泛型的语法糖,这种语法糖在有多个参数类型均是特性的情况下十分实用:
fn output3<T: Descriptive>(arg1: T, arg2: T) {
println!("{}", arg1.describe());
println!("{}", arg2.describe());
}
//特性作类型表示时如果涉及多个特性,可以用 + 符号表示,例如:
trait Summary {
fn describe(&self) -> String {
String::from("[Summary]")
}
}
trait Display {
fn describe(&self) -> String {
String::from("[Display]")
}
}
fn output4(item: impl Summary + Display) {}
fn output5<T: Summary + Display>(item: T) {}
struct Person {
name: String,
age: u8,
}
impl Descriptive for Person {
//如果想调用特性的默认接口,把下面这段代码去掉就可以了
fn describe(&self) -> String {
format!("{} -- {}", self.name, self.age)
}
}
impl Summary for Person {
fn describe(&self) -> String {
format!("{} -Summary- {}", self.name, self.age)
}
}
impl Display for Person {
fn describe(&self) -> String {
format!("{} -Display- {}", self.name, self.age)
}
}
/*/------------------
//复杂的实现关系可以使用 where 关键字简化,例如:
fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U)->i32{
}
//可以简化成:
fn some_function<T, U>(t: T, u: U) -> i32
where T: Display + Clone,
U: Clone + Debug
//------------------*/
impl Descriptive for () {
//如果想调用特性的默认接口,把下面这段代码去掉就可以了
fn describe(&self) -> String {
format!("6666666666666666666666")
}
}
let p = Person {
name: String::from("liujiayu"),
age: 30,
};
output(p);
//测试()实现特性
output2(());
let p = Person {
name: String::from("liujiayu"),
age: 30,
};
output4(p);
}
//特性做参数实现取最大数
fn test_4() {
// 由于需要声明 compare 函数的第二参数必须与实现该特性的类型相同,所以 Self (注意大小写)关键字就代表了当前类型(不是实例)本身。
trait Comparable {
fn compare(&self, object: &Self) -> i8;
}
fn max<T: Comparable>(array: &[T]) -> &T {
let mut max_index = 0;
let mut i = 1;
while i < array.len() {
if array[i].compare(&array[max_index]) > 0 {
max_index = i;
}
i += 1;
}
&array[max_index]
}
impl Comparable for f64 {
fn compare(&self, object: &f64) -> i8 {
if &self > &object {
1
} else if &self == &object {
0
} else {
-1
}
}
}
let arr = [1.0, 3.0, 5.0, 4.0, 2.0];
println!("maximum of arr is {}", max(&arr));
}
//特性做返回值
fn test_5() {
trait Descriptive {
fn describe(&self) -> String;
}
struct Person {
name: String,
age: u8,
}
impl Descriptive for Person {
fn describe(&self) -> String {
format!("{} {}", self.name, self.age)
}
}
fn person() -> impl Descriptive {
Person {
name: String::from("Cali"),
age: 24,
}
}
//但是有一点,特性做返回值只接受实现了该特性的对象做返回值且在同一个函数中所有可能的返回值类
//型必须完全一样。比如结构体 A 与结构体 B 都实现了特性 Trait,下面这个函数就是错误的:
struct A {}
struct B {}
impl Descriptive for A {
fn describe(&self) -> String {
String::from("A")
}
}
impl Descriptive for B {
fn describe(&self) -> String {
String::from("B")
}
}
// 编译不过
// fn some_function(bl:bool) -> impl Descriptive {
// if bl {
// return A {};
// } else {
// return B {};
// }
// }
//some_function(false);
}
//有条件实现方法
fn test_6() {
//impl 功能十分强大,我们可以用它实现类的方法。但对于泛型类来说,有时我们需要区分一下它所属的泛型已经实现的方法来决定它接下来该实现的方法
//这段代码声明了 A<T> 类型必须在 T 已经实现 B 和 C 特性的前提下才能有效实现此 impl 块。
//(编译不过,有机会再研究)
// struct A<T> {}
// impl<T: B + C> A<T> {
// fn d(&self) {}
// }
}
//trait中可以继承.继承的意义是什么,这个例子没有体现??????
fn test_7() {
println!("---------------test_7--------------");
trait PrintInfo {
/// 方法/函数,没有具体实现
fn info(&self) {
println!("base function---default");
}
}
// InheritTrait这个trait,继承PrintInfo
trait InheritTrait: PrintInfo {
fn out_info(&self) {
println!("inherit function---default");
}
}
#[derive(Debug)]
struct People {
id: u32, //id
name: &'static str, //姓名 字符串
sex: &'static str, //性别
}
impl PrintInfo for People {
fn info(&self) {
println!("base function---Nodefault")
}
}
impl InheritTrait for People {
fn out_info(&self) {
println!("inherit function---Nodefault");
}
}
let p = People {
id: 1,
name: "tom",
sex: "man",
};
p.info();
p.out_info();
}
//关联类型
fn test_8() {
println!("---------test_8-------");
/*
定义:关联类型(associated types)是一个将类型占位符与 trait 相关联的方式,这样trait的方法签名中就可以使用这些占位符类型。
trait的实现者会针对特定的实现在这个类型的位置指定相应的具体类型。如此可以定义一个使用多种类型的trait,直到实现此trait时都无需知道这些类型具体是什么。
我们阅读Rust程序的时候,有时候会出现如下的代码:
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
解释:
Iterator trait 有一个关联类型Item。Item是一个占位类型,同时next方法会返回Option<Self::Item>类型的值。
重点:"这个trait的实现者会指定Item的具体类型。"
自己的理解:
这就是一个迭代器,具体遍历的是啥暂时不写死,有可能是整数类型,也有可能是字符串类型
*/
pub trait Tt {}
impl Tt for i32 { }
impl Tt for String { }
/*
type Item : Tt;
Tt是Item的约束,必须是trait类型,代表的意思是指定Item类型A的时候,A必须实现了Tt才行
*/
pub trait Watch {
type Item : Tt;
fn inner(&self) -> Option<Self::Item>;
}
struct A {
data: i32,
}
impl Watch for A {
type Item = i32;
fn inner(&self) -> Option<Self::Item> {
Some(self.data)
}
}
struct B {
data: String,
}
impl Watch for B {
type Item = String;
fn inner(&self) -> Option<Self::Item> {
Some(self.data.clone())
}
}
let a = A{data: 10};
let b = B{data: String::from("B")};
assert_eq!(Some(10), a.inner());
assert_eq!(Some(String::from("B")), b.inner());
}
//trait继承的意义:子trait可以使用父trait的关联类型
fn test_9() {
println!("---------test_9-------");
trait tt1 {
type AccountID;
}
struct Empty{}
impl tt1 for Empty {
type AccountID = u64;
}
// InheritTrait这个trait,继承PrintInfo
trait tt11:tt1 {
fn out_info(&self);
}
impl tt11 for Empty {
fn out_info(&self) {
println!("inherit function---default");
let id:Self::AccountID = 1001;//注意带上Self:: 进行访问
}
}
}
//trait继承的意义:子trait可以使用父trait的关联函数
fn test_9_2() {
println!("---------test_9_2-------");
trait TraitA {
fn test_fn_traita();
fn test_fn_traita_self(&self);
}
trait TraitA2 {
fn test_fn_traita2();
}
trait TraitB: TraitA {
fn test_fn_traitb();
fn test_fn_traitb_self(&self);
}
trait TraitC: TraitB {
fn test_fn_traitc();
fn test_fn_traitc_self(&self);
}
struct StEmpty {}
impl TraitA for StEmpty {
fn test_fn_traita() {
println!("fn :test_fn_traita, trait : TraitA, struct :StEmpty");
}
fn test_fn_traita_self(&self) {
println!("fn :test_fn_traita_self, trait : TraitA, struct :StEmpty");
}
}
impl TraitB for StEmpty {
fn test_fn_traitb() {
println!("fn :test_fn_traitb, trait : TraitB, struct :StEmpty");
Self::test_fn_traita();
}
fn test_fn_traitb_self(&self) {
println!("fn :test_fn_traitb_self, trait : TraitB, struct :StEmpty");
//也可以调用带self的
self.test_fn_traita_self();
}
}
let t = StEmpty{};
t.test_fn_traitb_self();
impl TraitC for StEmpty {
fn test_fn_traitc() {
println!("fn :test_fn_traitc, trait : TraitC, struct :StEmpty");
Self::test_fn_traita(); //实验证明可以调用他爷爷的接口
}
fn test_fn_traitc_self(&self) {}
}
// 多重继承
trait TraitD: TraitA2 + TraitC {
fn test_fn_traitd();
}
impl TraitA2 for StEmpty {
fn test_fn_traita2() {
println!("fn :test_fn_traita2, trait : TraitA2, struct :StEmpty");
}
}
impl TraitD for StEmpty {
fn test_fn_traitd() {
println!("fn :test_fn_traitd, trait : TraitD, struct :StEmpty");
Self::test_fn_traita2();
Self::test_fn_traitc(); // TraitC
Self::test_fn_traitb(); // TraitC 的爸爸
Self::test_fn_traita(); // TraitC 的爷爷 - 都可以调用
}
}
StEmpty::test_fn_traitd();
}
//一个结构体有两个特性,他们的函数集有交集
fn test_10(){
println!("---------test_10-------");
trait TraitOne {
fn action(&self) {
println!("action of trait one!")
}
}
trait TraitTwo {
fn action(&self) {
println!("action of trait two!");
}
}
struct Person {}
impl TraitOne for Person {}
impl TraitTwo for Person {}
let p = Person {};
//p.action(); 编译失败,不支持这种调用方式
<Person as TraitOne>::action(&p);
<Person as TraitTwo>::action(&p);
}
fn test_11() {
/*
trait特性之间的关联
最初学习From和Into特性的时候,教程说只要实现了From特性就会自动实现Into特性,反之亦然。感觉很是奇怪,竟然还可以这样,
今天我们看一个类似的例子:只要实现了GetName特性也就自动实现了PrintName特性,不知道From和Into是不是这么做的,这个以
后有机会再研究把吧,
*/
trait GetName {
fn get_name(&self) -> &String;
}
trait PrintName {
fn print_name(&self);
}
//只要是实现了GetName特性的结构体,都可以实现PrintName
impl<T: GetName> PrintName for T {
fn print_name(&self) {
println!("name = {}", self.get_name());
}
}
///-----------------------下面是测试代码------------------
struct Student {
name: String,
}
impl GetName for Student {
fn get_name(&self) -> &String {
&(self.name)
}
}
let s = Student {
name: String::from("xiao ming"),
};
s.print_name();
}
fn main() {
test_1();
test_2();
test_3();
test_4();
test_5();
test_6();
test_7();
test_8();
test_9();
test_9_2();
test_10();
test_11();
}