trait(特征)类似于其他语言中的interface或者protocol,指定一个实际类型必须满足的功能集合
一、如何理解trait,可以从我们所了解的接口特性去推断trait的用法
1. 那么作为一个类接口的关键字,意味着被它修饰的类不包含实现的方法fn,只定义函数名称和参数,由这个类的实现类去完成它的方法。2. 任何实现接口的类都必须去实现接口的方法,这种特性恰好可以作为一种从上到下的约束,应用到Rust语法里面。3. 接口也可以不断被继承,最后实现类须要实现所有的接口里的方法。
二、trai的实现方式
1. 定义接口
trait HasArea { fn area(&self) ->f64;}
2. 实现接口
为一个结构体圆增加一个计算圆面积的函数:
struct Circle { x:f64, y:f64, radius:f64,}
impl HasAreaforCircle { fn area(&self) ->f64 { std::f64::consts::PI* (self.radius *self.radius) }}
----------------------------------------------------------
fn main() { let c =Circle { x:0.0f64, y:0.0f64, radius:1.0f64, }; println!("circle c has an area of {}", c.area());}
最终输出:circle c has an area of 1 3. 继承接口 trait Foo { fn foo(&self);}
trait FooBar:Foo { fn foobar(&self);}实现:struct Baz;
impl FooforBaz { fnfoo(&self) { println!("foo"); }}
impl FooBarforBaz { fn foobar(&self) { println!("foobar"); }}
4.自动化实现接口Rust会自动帮你实现接口,当然必须是某些特定的接口方法,就好像利用开发工具帮你实现一些接口方法一样。能帮你实现的方法仅限于:Clone,Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd这些方法一般是常见的方法,要自动实现上述方法,前提是使用derive属性。
#[derive(Debug)]structFoo;
fn main() { println!("{:?}",Foo);}
5.自带默认方法tait修饰的类都自动被系统加上一个默认方法,这个默认方法不要求被实现类实现,当然实现类可以去实现它并覆盖原有方法。
trait Foo { fn is_valid(&self) ->bool;
fn is_invalid(&self) ->bool { !self.is_valid() }}is_invalid是默认方法,Foo的实现者并不要求实现它,如果选择实现它,会覆盖掉它的默认行为。
三、Rust里面的应用场景
1.给泛型增加约束,或者说给泛型增加实现要求。例如:Debug是Rust内置的一个trait,为"{:?}"实现打印内容,函数foo接受一个泛型作为参数,并且约定其需要实现Debuguse std::fmt::Debug;
fn foo<T:Debug>(s:T) { println!("{:?}", s); }
2.给泛型增加多个trait,也即所谓的多重约束。
use std::fmt::Debug;
fn foo<T:Debug+Clone>(s:T) { s.clone(); println!("{:?}", s); }<T: Debug + Clone>中Debug和Clone使用+连接,表示泛型T需要同时实现这两个trait。
3. where关键字其实只是为多重约束增加一些写法。
标准写法:use std::fmt::Debug;
fn foo<T:Clone,K:Clone+Debug>(x:T, y:K) { x.clone(); y.clone(); println!("{:?}", y);}where从句写法一:fn foo<T,K>(x:T, y:K) whereT:Clone,K:Clone+Debug { x.clone(); y.clone(); println!("{:?}", y);}where从句写法二:fn foo<T,K>(x:T, y:K) whereT:Clone,K:Clone+Debug { x.clone(); y.clone(); println!("{:?}", y);}语法糖,这只是语法糖!
4.孤僻的应用场景,给内置类型增加trait
trait HasArea { fn area(&self) ->f64;}
impl HasAreafori32 { fn area(&self) ->f64 { *selfasf64 }}
area();
最后的输出接口f64类型的数值,这个方法相当有用
注意:1. 当你为某类型实现某 trait 的时候,必须要求类型或者 trait 至少有一个是在当前 crate 类库中定义的。你不能为第三方的类型实现第三方的 trait 。2. 在调用 trait 中定义的方法的时候,一定要记得让这个 trait 可被访问。
参考: