目录
给结构体添加方法有2种,一种是直接实现,一种的带trait的实现。
直接实现的方式中,数据和方法是对应的。带trait的实现中,trait是一组可以被共享的行为,只要实现了特征,你就能使用这组行为。
struct SearchData{
}
impl SearchData{
fn f(&self)-> i32{
return 123;
}
fn f2()-> i32{
return 456;
}
}
fn main() {
let x=SearchData{};
assert_eq!(x.f(), 123);
assert_eq!(SearchData::f2(), 456);
}
带trait的实现方式中,数据、接口描述、接口实现要分开。
trait Solution{
fn find(&self)-> i32;
}
struct SearchData{
}
impl Solution for SearchData{
fn find(&self)-> i32{
return 123;
}
}
fn main() {
let x=SearchData{};
println!("{}",x.find());
}
直接实现的方法会覆盖trait中的方法:
struct SearchData{
}
impl SearchData{
fn f1(&self)-> i32{
return 100;
}
fn f2(&self)-> i32{
return 222;
}
}
trait S{
fn f1(&self)-> i32;
fn f3(&self)-> i32;
}
impl S for SearchData{
fn f1(&self)-> i32{
return 111;
}
fn f3(&self)-> i32{
return 333;
}
}
fn main() {
let x=SearchData{};
println!("{} {} {}",x.f1(),x.f2(),x.f3());
}
输出:100 222 333
也就是说,代码可以正常运行,类型中非函数会覆盖trait中的同名函数。
被覆盖的函数也不是完全无法调用,只是不能用最简单的写法去调用,调用方法:
struct SearchData{
}
impl SearchData{
fn f1(&self)-> i32{
return 100;
}
fn f2()->i32{
return 222;
}
}
trait S{
fn f1(&self)-> i32;
fn f2()->i32;
}
impl S for SearchData{
fn f1(&self)-> i32{
return 111;
}
fn f2()->i32{
return 333;
}
}
fn main() {
let x=SearchData{};
println!("{} {}",x.f1(),S::f1(&x));
println!("{} {}",SearchData::f2(),<SearchData as S>::f2());
}
带self参数的,用S::f1方式去调用,不带self的,用<SearchData?as?S>::f2调用,相当于把具体的SearchData 类型向上转换成抽象的S类型之后再调用。
trait的具体实现会覆盖默认实现:
struct SearchData{
}
trait S{
fn f1(&self)-> i32{
return 222;
}
}
impl S for SearchData{
fn f1(&self)-> i32{
return 333;
}
}
fn main() {
let x=SearchData{};
println!("{}",x.f1());
}
输出333
2个trait之间的同名函数会冲突,造成编译失败:
struct SearchData{
}
trait S{
fn f1(&self)-> i32;
}
impl S for SearchData{
fn f1(&self)-> i32{
return 111;
}
}
trait S2:S{
fn f1(&self)-> i32;
}
impl S2 for SearchData{
fn f1(&self)-> i32{
return 222;
}
}
fn main() {
let x=SearchData{};
println!("{}",x.f1());
}
trait A{
fn funa();
}
trait B:A{
fn funb();
}
trait C:A+B{
fn func();
}
struct S{
}
impl A for S{
fn funa(){
println!("run a");
}
}
impl B for S{
fn funb(){
println!("run b");
}
}
impl C for S{
fn func(){
S::funa();
println!("run c");
}
}
注意,虽然有继承关系,但是可见性却是平级的,父特征中的方法也可以调用子特征中的方法。
例如,自定义排序是这么写的:
#[derive(Eq,PartialEq)]
struct Node{
id:i32,
num:i32
}
impl Ord for Node {
#[inline]
fn cmp(&self, other: &Node) -> Ordering {
if self.num != other.num{
return (*other).num.cmp(&self.num);
}else{
return self.id.cmp(&(*other).id);
}
}
}
impl PartialOrd for Node{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
return Some(self.cmp(other));
}
}
Ord依赖PartialOrd,但实现上却建议用partial_cmp调用cmp。
我是这么理解的,trait继承表达的是顶层的依赖关系,即设计层面的依赖关系,而trait内的函数调用是底层的依赖关系,即实现层面的依赖关系。
孤儿规则:如果你想要为类型 A 实现特征 T,那么 A 或者 T 至少有一个是在当前作用域中定义的。
这条规则保证了代码的安全性,使得无关的代码不会互相影响。
?以减法为例
pub trait Sub<Rhs = Self> {
type Output;
fn sub(self, rhs: Rhs) -> Self::Output;
}
用法:
struct S{
x:i32,
y:f32
}
impl Sub for S{
type Output = Self;
fn sub(self,rhs:Self)->Self::Output{
S{x:self.x-rhs.x,y:self.y-rhs.y}
}
}