rust 面向对象、trait

发布时间:2023年12月17日

目录

1,结构体、成员方法

2,trait(特征)

3,同名函数的覆盖、冲突

4,trait继承

5,trait的孤儿规则

6,trait的type成员


给结构体添加方法有2种,一种是直接实现,一种的带trait的实现。

直接实现的方式中,数据和方法是对应的。带trait的实现中,trait是一组可以被共享的行为,只要实现了特征,你就能使用这组行为。

1,结构体、成员方法


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);
}

2,trait(特征)

带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());
}

3,同名函数的覆盖、冲突

直接实现的方法会覆盖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());
}

4,trait继承

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内的函数调用是底层的依赖关系,即实现层面的依赖关系

5,trait的孤儿规则

孤儿规则:如果你想要为类型 A 实现特征 T,那么 A 或者 T 至少有一个是在当前作用域中定义的。

这条规则保证了代码的安全性,使得无关的代码不会互相影响。

6,trait的type成员

?以减法为例

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}
    }
}

文章来源:https://blog.csdn.net/nameofcsdn/article/details/135033684
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。