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
基本使用,暂时就想到这么多