【百例RUST - 014】Trait

【百例RUST - 014】Trait

第一章 基础概述

第01节 基础介绍

复制代码
trait 定义了一组可以被共享的行为, 只要实现了 trait 就可以在代码当中, 使用这组行为。 他类似于其他语言的接口(interface)

1、可以通过 trait 以抽象的方式定义共享的行为
2、可以使用 trait bounds 指定泛型是任何拥有特定行为的类型。

第02节 定义格式

定义 trait 就是定义一组行为,代码如下:

rust 复制代码
// 下面的代码, 定义了一个叫做 GetInformation 的 trait 
// 该 trait 提供了 get_name 和 get_age 方法
pub trait GetInformation {
    fn get_name(&self) -> &String;
    fn get_age(&self) -> u32;
} 

第03节 入门案例

案例代码

rust 复制代码
// 下面的代码, 定义了一个叫做 GetInformation 的 trait 
// 该 trait 提供了 get_name 和 get_age 方法
pub trait GetInformation {
    fn get_name(&self) -> &String;
    fn get_age(&self) -> u32;
} 


// 定义结构体 学生
pub struct Student{
    pub name : String,
    pub age  : u32,
}

// 为结构体 学生类型实现 GetInformation trait
impl GetInformation for Student{
    fn get_name(&self) -> &String {
        &self.name
    }
    fn get_age(&self) -> u32 {
        self.age
    }
}

// 定义结构体 老师
pub struct Teacher{
    pub name : String,
    pub age  : u32,
}

// 为结构体 老师类型实现 GetInformation trait
impl GetInformation for Teacher{
    fn get_name(&self) -> &String {
        &self.name
    }
    fn get_age(&self) -> u32 {
        self.age
    }
}


fn main(){
   // 构建学生的对象
   let s: Student = Student{ name: "zhangsan".to_string(), age: 19};
   println!("s.name={}, s.age={}" , s.get_name(), s.get_age());
   let t : Teacher = Teacher { name: "fawaikuangtu".to_string(), age: 40 };
   println!("t.name={}, t.age={}" , t.get_name(), t.get_age());
}

// s.name=zhangsan, s.age=19
// t.name=fawaikuangtu, t.age=40

第04节 默认实现

可以在定义 traint 的时候,定义默认的实现方式,trait 的类型可以使用默认的行为。

整体案例代码如下

rust 复制代码
// 下面的代码, 定义了一个叫做 GetInformation 的 trait 
// 在定义 trait 的时候, 可以定义默认的实现方案
pub trait GetInformation {
    fn get_name(&self) -> &String;
    fn get_age(&self) -> u32{
        return 18;
    }
}

// 定义学生结构体
struct Student{
    name:String,
    age:u32,
}

// 学生结构体的实现
impl GetInformation for Student{
    fn get_name(&self) -> &String {
        &self.name
    }
    // 学生未实现了 get_age 函数
}

// 定义老师结构体
struct Teacher{
    name:String,
    age:u32,
}

// 老师结构体的实现
impl GetInformation for Teacher{
    fn get_name(&self) -> &String {
        &self.name
    }

    // 老师实现了 get_age 函数
    fn get_age(&self) -> u32 {
        self.age
    }
}



fn main(){
    // 构建学生对象
    let s: Student = Student{name:"zhangsan".to_string(), age:28};
    println!("s.name={}, s.age={}", s.get_name(), s.get_age());

    // 构建老师对象
    let t: Teacher = Teacher{name:"fawaikuangtu".to_string(), age:48};
    println!("t.name={}, t.age={}", t.get_name(), t.get_age());
}

//  s.name=zhangsan, s.age=18
//  t.name=fawaikuangtu, t.age=48

第二章 函数上使用

第01节 Trait 作为参数

案例代码

rust 复制代码
// 下面的代码, 定义了一个叫做 GetInformation 的 trait 
// 在定义 trait 的时候, 可以定义默认的实现方案
pub trait GetInformation {
    fn get_name(&self) -> &String;
    fn get_age(&self) -> u32{
        return 18;
    }
}

// 定义学生结构体
struct Student{
    name:String,
    age:u32,
}

// 学生结构体的实现
impl GetInformation for Student{
    fn get_name(&self) -> &String {
        &self.name
    }
    // 学生未实现了 get_age 函数
}

// 定义老师结构体
struct Teacher{
    name:String,
    age:u32,
}

// 老师结构体的实现
impl GetInformation for Teacher{
    fn get_name(&self) -> &String {
        &self.name
    }

    // 老师实现了 get_age 函数
    fn get_age(&self) -> u32 {
        self.age
    }
}


// 将 Trait 作为函数的参数使用
fn print_information(item: impl GetInformation){
    println!("name = {}, age = {}", item.get_name(), item.get_age());
}


fn main(){
    // 构建学生对象
    let s: Student = Student{name:"zhangsan".to_string(), age:28};
    print_information(s);

    // 构建老师对象
    let t: Teacher = Teacher{name:"fawaikuangtu".to_string(), age:48};
    print_information(t);
     
}

//  name = zhangsan, age = 18
//  name = fawaikuangtu, age = 48

第02节 Trait的bound语法

语法

rust 复制代码
// 【语法一】将 Trait 作为函数的参数使用
fn print_information1<T: GetInformation>(item: T){
    println!("name = {}, age = {}", item.get_name(), item.get_age());
}


// 【语法二】将 Trait 作为函数的参数使用
fn print_information2<T>(item: T) where T: GetInformation{
    println!("name = {}, age = {}", item.get_name(), item.get_age());
}

案例代码

rust 复制代码
// 下面的代码, 定义了一个叫做 GetInformation 的 trait 
// 在定义 trait 的时候, 可以定义默认的实现方案
pub trait GetInformation {
    fn get_name(&self) -> &String;
    fn get_age(&self) -> u32{
        return 18;
    }
}

// 定义学生结构体
struct Student{
    name:String,
    age:u32,
}

// 学生结构体的实现
impl GetInformation for Student{
    fn get_name(&self) -> &String {
        &self.name
    }
    // 学生未实现了 get_age 函数
}

// 定义老师结构体
struct Teacher{
    name:String,
    age:u32,
}

// 老师结构体的实现
impl GetInformation for Teacher{
    fn get_name(&self) -> &String {
        &self.name
    }

    // 老师实现了 get_age 函数
    fn get_age(&self) -> u32 {
        self.age
    }
}


// 将 Trait 作为函数的参数使用
fn print_information1<T: GetInformation>(item: T){
    println!("name = {}, age = {}", item.get_name(), item.get_age());
}

// 将 Trait 作为函数的参数使用
fn print_information2<T>(item: T) where T: GetInformation{
    println!("name = {}, age = {}", item.get_name(), item.get_age());
}


fn main(){
    // 构建学生对象
    let s: Student = Student{name:"zhangsan".to_string(), age:28};
    print_information1(s); 

    // 构建老师对象
    let t: Teacher = Teacher{name:"fawaikuangtu".to_string(), age:48};
    print_information2(t);
     
}

//  name = zhangsan, age = 18
//  name = fawaikuangtu, age = 48

第03节 多个 Trait 相加

案例代码

rust 复制代码
// 定义两个 trait 都为 结构体 student 来服务
pub trait GetName {
    fn get_name(&self) -> &String;
}
pub trait GetAge {
    fn get_age(&self) -> u32;
}

// 定义学生结构体
struct Student{
    name:String,
    age:u32,
}

// 学生结构体的实现
impl GetName for Student{
    fn get_name(&self) -> &String {
        &self.name
    }
}

// 学生结构体的实现
impl GetAge for Student{
    fn get_age(&self) -> u32 {
        return self.age;
    }
}


// 将 Trait 作为函数的参数使用
fn print_information<T: GetName + GetAge>(item: T){
    println!("name = {}, age = {}", item.get_name(), item.get_age());
}
 


fn main(){
    // 构建学生对象
    let s: Student = Student{name:"zhangsan".to_string(), age:28};
    print_information(s); 
}

//  name = zhangsan, age = 28

一个结构体 struct,可以实现多个特征。 trait

如果要多个都采用的情况下,可以使用 + 连接

第04节 返回 trait 的类型

案例代码

rust 复制代码
// trait 类型可以作为函数的返回值类型, 示例代码如下:
pub trait GetName{
    fn get_name(&self) -> &String;
}

struct Student{
    name: String,
}

impl GetName for Student {
    fn get_name(&self) -> &String {
        &self.name
    }
}

// 将 trait 作为函数的返回值
fn produce_item_with_name() -> impl GetName {
    Student {
        name: "zhangsan".to_string(),
    }
}

fn main(){
    // 构建学生对象
    let s = produce_item_with_name();
    println!("name: {}", s.get_name());
}

//  name: zhangsan

第三章 Trait 两种用法

第01节 Traint 的 bound 有效条件实现

通过使用带有 trait bound 的泛型参数的 impl 块,可以有效条件只为那些实现了特定 trait 的类型实现方法。

案例代码

rust 复制代码
// 泛型 特征 的高度结合
// 1、 定义两个特征
trait GetName{
    fn get_name(&self) -> &String;
}

trait GetAge{
    fn get_age(&self) -> u32;
}

// 2、定义老师和学生
struct Teacher{
    name:String,
    age:u32,
}
impl GetName for Teacher {
    fn get_name(&self) -> &String {
        &self.name
    }
}
impl GetAge for Teacher {
    fn get_age(&self) -> u32 {
        self.age
    }
}

struct Student{
    name:String,
    age:u32,
}

impl GetName for Student {
    fn get_name(&self) -> &String {
        &self.name
    }
}

impl GetAge for Student{
    fn get_age(&self) -> u32 {
        self.age
    }
}

// 3、定义结构体, 包含两个对象
struct  PeopleInfo <T, U>{
    master: T,
    emp: U,
}

impl  <T, U> PeopleInfo<T, U> where T : GetName+GetAge, U: GetName + GetAge{
    fn print_all(& self){
        println!("master.name = {}", self.master.get_name());
        println!("master.age = {}", self.master.get_age());
        println!("emp.name = {}", self.emp.get_name());
        println!("emp.age = {}", self.emp.get_age());
    }
}

fn main(){
    let s:Student = Student{
        name: String::from("zhangsan"),
        age  : 12,
    };
    let t : Teacher = Teacher { 
        name: String::from("zhangwuji"), 
        age: 32,
     };

     let m: PeopleInfo<Teacher, Student>  = PeopleInfo{
        master: t,
        emp: s,
     };
     m.print_all();
}


// master.name = zhangwuji
// master.age = 32
// emp.name = zhangsan
// emp.age = 12

第02节 对任何实现了特定 trait 的类型有效条件使用 trait

在 rust 当中,比较常见的操作 trait 的用法: 对实现了特定 trait 的类型,有效条件的,实现 trait

案例代码如下:

rust 复制代码
// trait 的定义 
pub trait GetName {
    fn get_name(&self) -> &String;
}

// trait 的定义 
pub trait  PrintName {
    fn print_name(&self);
}

// 关联 两个 trait : PrintName 和 GetName
impl <T: GetName> PrintName for T {
    fn print_name(&self) {
        println!("name = {}", self.get_name());
    }
}

// 将为 Student 实现对应的 trait
struct Student {
    name:String,
}

impl GetName for Student {
    fn get_name(&self) -> &String {
        &self.name
    }
}

fn main(){
    let s: Student = Student{
        name: String::from("zhangsan"),
    };
    // student 实现了 GetName trait 因此可以直接调用 PrintName trait 中的函数 print_name
    s.print_name();
}

//  name = zhangsan

调用链路,关于 s.print_name()

复制代码
1、查找方法:
	编译器发现 s (Student类型) 正在调用一个叫做 print_name 的方法
	
2、检查直接实现:
	编译器查看 Student 本身有没有定义 print_name ?  答案是没有的
	
3、检查 Trait 的实现
	A. 编译器发现有一个 Trait 叫做 PrintName 包含了这个方法
	B. 编译器看到有一个 "毯式实现"  任何满足 GetName 的类型都会拥有 PrintName 

4、验证约束
	A. Student 实现了 GetName 吗? 是的 我们在代码当中手动写上了 impl GetName for Student
	B. 结论: Student 自动获得了 PrintName 的所有方法
	
5、执行代码
	进入 print_name 内容, 他调用了 self.get_name() 
	因为 Student 实现了 GetName 这个调用是合法的, 最终打印出 "zhangsan"

为什么这种写法很优雅呢

复制代码
通过这种方式,  s.print_name() 体现了代码的高度解耦:

1、功能分离 GetName 负责如何获取数据, PrintName 负责如何展示数据
2、极强的扩展性, 如果我以后增加了一个 Teacher 的结构体, 我们只需要给 Teacher 实现 GetName 
   他就会自动具备了 print_name 的功能, 而不需要我们再重复写一遍打印逻辑。
相关推荐
Titan20248 分钟前
HTTPS基础知识梳理
服务器·网络·c++·网络协议·学习·http·https
智脑API14 分钟前
CCSwitch Claude Code 如何配置 MCP?服务器启动、工具权限与安全测试
运维·服务器·claude·codex·ccswitch
自然石人15 分钟前
石都随笔:深耕不张扬,平凡自有千钧力
网络·经验分享·百度·传媒·新浪微博
新时代牛马37 分钟前
epoll 源码路径:从epoll_ctl 到ep_poll 的就绪唤醒
网络·数据库·网络协议
简单Janeee38 分钟前
[Vue 3 从零到上线]-第九篇:更美更强——引入 UI 库与网络请求 (Axios)
网络·vue.js·ui
学习星球41 分钟前
空天地一体化网络(NTN)深度解析:从Starlink D2C到3GPP NTN,卫星直连手机是如何实现的?
网络·人工智能·算法·智能手机·php
DolitD1 小时前
云流技术深度剖析:单服务器下如何实现3D应用的多实例并发?
java·服务器·前端·3d·云原生·云计算
jun_bai1 小时前
Ecplise中发布项目到配置的tomcat中
java·服务器
芦柑4641 小时前
画布和3D导演台工具:短剧分镜从素材整理到空间预演的完整链路
服务器·前端·数据库
闲云野鹤在人间1 小时前
KVM虚拟化实战|CentOS‑Stream8 两种安装方式+模板制作+基础使用
linux·运维·服务器·centos