rust中Trait的基本使用

1.trait的基本使用

最基本的trait

struct Person {
    name:String,
    id:i32,
}
struct Teacher {
    name:String,
    id:i32,
}
trait sayHello {
    fn say_hello(&self) {
        println!("Hello!");
    }
}
//Person没有重载,就用的默认实现
impl sayHello for Person{}
impl sayHello for Teacher{
    //teacher进行了重载,输出不同了
    fn say_hello(&self) {
        println!("Hello,I'm {}",self.name);
    }
}
fn main() {
    let p = Person{name:String::from("lisi"),id:5};
    p.say_hello();
    let t = Teacher{name:String::from("Mrs.Li"),id:1};
    t.say_hello();
}
/*
输出如下:
Hello!
Hello,I'm Mrs.Li
这都是显而易见的。
*/

那如果person和teacher本来就有同名的方法呢?

struct Person {
    name:String,
    id:i32,
}
impl Person {
    fn say_hello(&self) {
        println!("this is person.say_hello");
    }
}
struct Teacher {
    name:String,
    id:i32,
}
impl Teacher {
    fn say_hello(&self) {
        println!("this is teacher.say_hello");
    }
}
trait sayHello {
    fn say_hello(&self) {
        println!("Hello!");
    }
}
//Person没有重载,就用的默认实现
impl sayHello for Person{}
impl sayHello for Teacher{
    //teacher进行了重载,输出不同了
    fn say_hello(&self) {
        println!("Hello,I'm {}",self.name);
    }
}
fn main() {
    let p = Person{name:String::from("lisi"),id:5};
    p.say_hello();
    let t = Teacher{name:String::from("Mrs.Li"),id:1};
    t.say_hello();
}
/*
输出如下
this is person.say_hello
this is teacher.say_hello
会优先调用类型本身的方法
*/

那么,如何再调用trait的方法呢

sayHello::say_hello(&p);
sayHello::say_hello(&t);
/*
只需要在末尾添加这两行,传入对象的引用,就可以进行调用了。
输出如下:
this is person.say_hello
this is teacher.say_hello
Hello!
Hello,I'm Mrs.Li
*/

再者,实现了trait的类型可以转变为trait对象。就像其他语言的接口或者多态一样。如何实现呢

fn say_hello(s:&dyn sayHello) {
    s.say_hello();
}
//并且在main的末尾添加如下代码即可
	say_hello(&p);
    say_hello(&t);
/*
总的输出如下:
this is person.say_hello
this is teacher.say_hello
Hello!
Hello,I'm Mrs.Li
Hello!
Hello,I'm Mrs.Li
*/

或者,直接进行类型转换也可,最终代码如下

struct Person {
    name:String,
    id:i32,
}
impl Person {
    fn say_hello(&self) {
        println!("this is person.say_hello");
    }
}
struct Teacher {
    name:String,
    id:i32,
}
impl Teacher {
    fn say_hello(&self) {
        println!("this is teacher.say_hello");
    }
}
trait sayHello {
    fn say_hello(&self) {
        println!("Hello!");
    }
}
//Person没有重载,就用的默认实现
impl sayHello for Person{}
impl sayHello for Teacher{
    //teacher进行了重载,输出不同了
    fn say_hello(&self) {
        println!("Hello,I'm {}",self.name);
    }
}
fn say_hello(s:&dyn sayHello) {
    s.say_hello();
}
fn main() {
    let p = Person{name:String::from("lisi"),id:5};
    p.say_hello();
    let t = Teacher{name:String::from("Mrs.Li"),id:1};
    t.say_hello();
    sayHello::say_hello(&p);
    sayHello::say_hello(&t);
    say_hello(&p);
    say_hello(&t);
    //直接转换类型
    let p1:& dyn sayHello = &p;
    p1.say_hello();
    say_hello(p1);
    let t1:& dyn sayHello = &t;
    t1.say_hello();
    say_hello(t1);

}
/*
输出如下:
this is person.say_hello
this is teacher.say_hello
Hello!
Hello,I'm Mrs.Li
Hello!
Hello,I'm Mrs.Li
Hello!
Hello!
Hello,I'm Mrs.Li
Hello,I'm Mrs.Li
*/

当然了,还有其他方法实现,比如泛型约束

fn hello<T:sayHello>(s:&T) {
    s.say_hello();
}
fn main() {
    //上面的代码不加赘述,只在末尾添加代码。
    hello(&p);
    hello(&t);
}
/*
最终输出如下:
this is person.say_hello
this is teacher.say_hello
Hello!
Hello,I'm Mrs.Li
Hello!
Hello,I'm Mrs.Li
Hello!
Hello!
Hello,I'm Mrs.Li
Hello,I'm Mrs.Li
Hello!
Hello,I'm Mrs.Li
*/

trait中还有其他一些常见的名称,比如type、self、Self等

use std::any::type_name;
//输出变量类型
fn print_type_of<T>(_: &T) {
    println!("{}", type_name::<T>());
}

struct bird{
    id:i32,
}
impl bird {
    fn print_id(&self) {
        println!("bird's id is {}",self.id);
    }
    fn print_type(&self) {
        print_type_of(&self);
    }
}
trait fly {
    type Bird;
    fn fly_high(&self) ->Self;
    fn fly_low(&self) ->Self::Bird;
}
impl fly for bird {
    //关联类型可以定义为自己需要的类型,假设还有个和bird一样的结构体,叫tiger,那么在为tiger实现trait的时候
    //这里type Bird = tiger也是可以的
    type Bird = bird;
    fn fly_high(&self) ->Self {
        println!("bird flies high!");
        Self{id:6}
    }
    fn fly_low(&self) ->Self::Bird {
        println!("bird flies low!");
        Self::Bird{id:9}
    }
}
struct tiger{
    id:i32,
}
impl tiger{
    //和上面的&self是等价的
    fn print_id(self:&Self) {
        println!("this is tiger,id is {0}",self.id);
    }
    fn print_type(self:&Self) {
        print_type_of(&self);
    }
}
impl fly for tiger {
    type Bird = tiger;
    fn fly_high(&self) -> Self {
        println!("如虎添翼,所以飞得高");
        tiger {id:66}
    }
    fn fly_low(&self) ->Self::Bird {
        println!("有翅膀,不过太小了,飞不高");
        Self::Bird{id:16}
    }
}
fn main() {
    let b1 = bird{id:1};
    let b2 = b1.fly_high();
    let b3 = b1.fly_low();
    b1.print_id();
    b2.print_id();
    b3.print_id();
    let t1 = tiger{id:1};
    let t2 = t1.fly_high();
    let t3 = t2.fly_low();
    t1.print_id();
    t2.print_id();
    t3.print_id();
    println!("============");
    b1.print_type();
    t1.print_type();
}
/*
输出如下:
bird flies high!
bird flies low!
bird's id is 1
bird's id is 6
bird's id is 9
如虎添翼,所以飞得高
有翅膀,不过太小了,飞不高
this is tiger,id is 1
this is tiger,id is 66
this is tiger,id is 16
============
&trait_sum::bird
&trait_sum::tiger

*/
rust里面,对象的方法,第一个参数始终是类似于&self的写法,和self:&Self等价
在什么对象中,Self就等于什么类型,self就等于那个对象。
比如 let b = bird{id:1}。在它的impl或者它实现的Trait里面,Self就等价于bird,self就等价于b
再一个就是trait里面的type Bird,称之为关联类型。它可以在实现trait的时候定义为自己需要的类型,非常灵活。

这里提到了关联类型,下面就举例一个使用关联类型的Add,Trait。实现了Add这个Trait,就可以使用add()或者直接对象相加(+)

use std::any::type_name;
use std::ops::Add;
//输出变量类型
fn print_type_of<T>(_: &T) {
    println!("{}", type_name::<T>());
}
#[derive(Debug,Clone)]
struct team{
    score:i32
}
impl Add for team {
    type Output = i64;
    fn add(self,other:Self) -> Self::Output {
        (self.score+other.score) as Self::Output
    }
}
fn main() {
    let t1 = team{score:66};
    let t2 = team{score:88};
    let r1 = t1.clone().add(t2.clone());
    let r2 = t1.clone()+t2.clone();
    println!("{}",r1);
    println!("{}",r2);
    print_type_of(&r1);
    print_type_of(&r2);
    println!("{:?}",t1);
    println!("{:?}",t2);
}
/*
输出如下:
154
154
i64
i64
team { score: 66 }
team { score: 88 }
可见,trait的关联类型是很有效的;并可以加深对self和Self关键字的理解
*/
#[derive(Debug,Clone)]
这里,对team结构体派生了基本trait,这样,才可以使用{:?}输出team对象。
派生了Clone特征,team对象才能调用clone()
否则的话,let r1 = t1.add(t2)。t1、t2都失去所有权了

既然讲到了派生基本trait,那么再举例实现一些基本trait

以下为常用的基本trait实现

1.Debug、Display和ToString

use std::fmt;
use std::any::type_name;
use std::ops::Add;
//输出变量类型
fn print_type_of<T>(_: &T) {
    println!("{}", type_name::<T>());
}
struct Point {
    x:i32,
    y:i32,
}
// 实现 Debug trait,不用派生,就可以直接使用{:?}输出
impl fmt::Debug for Point {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Point {{ x: {}, y: {} }}", self.x, self.y)
    }
}

// 实现 Display trait,不用调试,就可以直接使用{}输出
impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({},{})", self.x, self.y)
    }
}
//实现 ToString trait,但要注意的是会和Display冲突。因为实现了Display也会自动实现ToString
/*impl ToString for Point {
    fn to_string(&self) ->String {
        format!("x is {},y is {}",self.x,self.y)
    }
}*/
fn main() {
    let p = Point{x:7,y:9};
    println!("{:?}",p);
    println!("{}",p);
    let s:String = p.to_string();
    println!("{}",s);
}
//如果启用了ToString,注释Dispaly,那么将不能直接输出p,但是s会使用ToString实现转换

2.deref trait

//上面的例子,本来想实现i32+i32=i64的,但是会报错。也就是trait和struct,至少一个需要定义在当前trait,也叫做孤儿规则
// impl doesn't use only types from inside the current crate
impl Add for i32 {
    type Output = i64;

    fn add(self, other: Self) -> Self::Output {
        (self + other) as Self::Output
    }
}

那有没有办法来实现呢,当然是有的,但是需要绕一下

第一种:元组结构体(都快忘记了,不需要实现deref)

#[derive(Debug,Clone)]
struct I32(i32);
impl Add for I32 {
    type Output = i64;
    fn add(self,other: Self)->Self::Output {
        (self.0 + other.0) as Self::Output
    }
}
fn main() {
    let n1 = I32(66);
    let n2 = I32(88);
    let r = n1.clone()+n2.clone();
    println!("{}",r);
    print_type_of(&r);
    println!("{:?}",n1);
    println!("{:?}",n2);
}
/*
154
i64
I32(66)
I32(88)
*/

第二种,利用Deref,便不用在add中指定某个成员了。只需要在Deref中指定一次就好了。

#[derive(Debug)]
struct Wrap(i32);
impl Deref for Wrap {
    type Target = i32;
    fn deref(&self) ->&Self::Target {
        &self.0
    }
}
impl Add for Wrap {
    type Output = i64;
    fn add( self, other:Self) ->Self::Output {
        (*self + *other) as i64
    }
}
fn main() {
    let mut w1 = Wrap(99);
    let mut w2 = Wrap(66);
    let r = w1.abs() + w2.abs();
    println!("{}",r);
    w1.0 = 88;
    let result = *w1 + *w2;
    println!("{}",result);
    print_type_of(&w1);
    w2.0 = 166;
    let r = w1 + w2;
    println!("{}",r);
    print_type_of(&r);
    // println!("{:?}",w1);	//上面的+,已经把它的所有权移动了。
}
一定程度上,为使用它包裹的i32提供了便利。可以自己重载运算符。也可以直接调用i32自带的方法。

3.Clone Trait

上面自定义派生了这么多次Clone,现在,我们来自己实现一下
use std::ops::Deref;
use std::fmt;
use std::any::type_name;
use std::ops::Add;
//输出变量类型
fn print_type_of<T>(_: &T) {
    println!("{}", type_name::<T>());
}
#[derive(Debug)]
struct I32(i32);
impl Add for I32 {
    type Output = i64;
    fn add(self,other: Self)->Self::Output {
        (self.0 + other.0) as Self::Output
    }
}
impl Clone for I32 {
    fn clone(&self)->Self {
        Self(self.0)
    }
}
fn main() {
    let mut m = I32(9);
    let mut n = m.clone();
    m.0 = 16;
    n.0 = 18;
    println!("{:?}",m);
    println!("{:?}",n);
    let r = m.clone() + n.clone();//可以调用clone,这样还能使用m和n
    print_type_of(&r);
    println!("{}",r);
}
/*
I32(16)
I32(18)
i64
34
*/

4.Copy Trait

如果只实现Clone的话,我们在Clone的代码的main末尾添加3句。
	let x = m;
    println!("{:?}",x);
    println!("{:?}",m);
//立马就会报错。 println!("{:?}",m);
    //                     ^ value borrowed here after move

现在,我们为I32再实现Copy

use std::ops::Deref;
use std::fmt;
use std::any::type_name;
use std::ops::Add;
//输出变量类型
fn print_type_of<T>(_: &T) {
    println!("{}", type_name::<T>());
}
#[derive(Debug)]
struct I32(i32);
impl Add for I32 {
    type Output = i64;
    fn add(self,other: Self)->Self::Output {
        (self.0 + other.0) as Self::Output
    }
}
impl Clone for I32 {
    fn clone(&self)->Self {
        Self(self.0)
    }
}
//实现Copy需要先实现Clone
impl Copy for I32 {

}
fn main() {
    let mut m = I32(9);
    let mut n = m.clone();
    m.0 = 16;
    n.0 = 18;
    println!("{:?}",m);
    println!("{:?}",n);
    let r = m + n;//这样,我们就不用显示调用clone()方法了。
    print_type_of(&r);
    println!("{}",r);
    let x = m;
    m.0 = 66;
    println!("{:?}",x);
    println!("{:?}",m);
}
/*
I32(16)
I32(18)
i64
34
I32(16)
I32(66
*/

5.From Trait 和Into Trait

实现了From Trait,也自动实现了Into
use std::ops::Deref;
use std::fmt;
use std::any::type_name;
use std::ops::Add;
//输出变量类型
fn print_type_of<T>(_: &T) {
    println!("{}", type_name::<T>());
}
#[derive(Debug)]
struct I32(i32);
impl Add for I32 {
    type Output = i64;
    fn add(self,other: Self)->Self::Output {
        (self.0 + other.0) as Self::Output
    }
}
impl Clone for I32 {
    fn clone(&self)->Self {
        Self(self.0)
    }
}
//实现Copy需要先实现Clone
impl Copy for I32 {

}
impl From<i32> for I32 {
    fn from(value:i32) -> Self {
        Self(value)
    }
}
fn main() {
    let m = I32::from(99 as i32);
    println!("{:?}",m);
    print_type_of(&m);
    let n:I32 = 66.into();
    println!("{:?}",n);
    print_type_of(&n);
}
/*
I32(99)
trait_sum::I32
I32(66)
trait_sum::I32
*/

6.Drop Trait

use std::ops::Deref;
use std::fmt;
use std::any::type_name;
use std::ops::Add;
//输出变量类型
fn print_type_of<T>(_: &T) {
    println!("{}", type_name::<T>());
}
#[derive(Debug,Clone)]
struct MyI32 {
    num:i32,
}
/*
impl Copy for I32 {
    |               ^^^ `Copy` not allowed on types with destructors
没想到没有这么简单,实现了Copy Trait的类型,不能实现Drop
impl Drop for I32 {
    fn drop(&mut self) {
        println!("this is drop");
    }
}
*/
impl Drop for MyI32 {
    fn drop(&mut self) {
        println!("this is drop");
    }
}
fn main() {
    let m = MyI32{num:66};
    // drop(m);    //^ value borrowed here after move
    core::mem::drop(m);  //和上面一样的效果
    // println!("{:?}",m); 显示调用drop,会直接丧失所有权,不能再使用
}
//输出: this is drop

基本使用,暂时就想到这么多

posted @ 2024-10-29 15:05  念秋  阅读(22)  评论(0编辑  收藏  举报