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接受一个泛型作为参数,并且约定其需要实现Debug
use 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 可被访问。
参考:https://github.com/rustcc/RustPrimer/blob/master/10-trait/10-00-overview.md