Rust学习笔记007:Trait --- Rust的“接口”

news2024/10/6 19:13:46

Trait

  • 在Rust中,Trait(特质)是一种定义方法集合的机制,类似于其他编程语言中的接口(java)或抽象类(c++的虚函数)。

。Trait 告诉 Rust 编译器:

  • 某种类型具有哪些并且可以与其它类型共享的功能
  • Trait:抽象的定义共享行为
  • Trait bounds(约束): 泛型类型参数指定为实现了特定行为的类型。Trait 与其它语言的接口 (interface) 类似,但有些区别。

定义Trait

trait MyTrait { //关键字trait(线条、特征、勾勒)
    // 定义Trait的方法
    fn my_method(&self);//只有方法签名,没有具体实现,实现该trait的类型必须提供具体实现

    // 可选:可以在这里定义其他方法
    fn my_method_1(&self)-> String {
        String::from("也可以提供默认实现")
    };
}

实现Trait

struct MyStruct;

impl MyTrait for MyStruct {
    fn my_method(&self) {
        println!("This is the implementation for my_method in MyStruct");
    }
}

孤儿规则

  • 可在某个类型上实现trait:

    • 类型或者trait在当前作用域中定义的
  • 无法为外部类型来实现外部的trait:

    • 这个限制是程序属性的一部分(也就是一致性)。
    • 更具体的说是孤儿规则:之所以这样命名是因为父类型不存在。
    • 此规则确保其他人的代码不能破坏您的diamond,反之亦然。
    • 如果没有这个规则,两个crate可以为同一个理性实现同一个trait,Rust就不知道该使用哪个实现了。
    • 如果允许任意 crate 对任意类型实现任意 trait,可能会导致多个 crate 中的相同类型实现相同的 trait,这会引起冲突和不可预期的行为。

引入与使用Trait

简单示例:

// 定义一个 Trait
trait Animal {
    fn make_sound(&self);
}

// 实现 Trait for struct Dog
struct Dog;

impl Animal for Dog {
    fn make_sound(&self) {
        println!("Woof!");
    }
}

// 使用 Trait 对象
fn print_sound(a: &dyn Animal) {
    a.make_sound();
}

fn main() {
    let dog = Dog;
    // 两种调用方法
    dog.make_sound(); // 输出: Woof!
    print_sound(&dog); // 输出: Woof!
}

将trait打包到库中

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

Trait作为参数

在这里插入图片描述

使用多个Trait

// 定义两个trait,一个“总结”,一个“展示”
trait Summary {
    fn summarize(&self) -> String;
}
trait Display {
    fn display(&self);
}



// 定义结构体 CCTV
struct CCTV {
    author: String,   content: String,
}
// 为CCTV实现总结功能
impl Summary for CCTV {
    fn summarize(&self) -> String {
        format!("{}, by {}", self.content, self.author)
    }
}
// 为CCTV实现展示功能
impl Display for CCTV {
    fn display(&self) {
        println!("Author: {}", self.author);
        println!("Content: {}", self.content);
    }
}



// 定义一个函数,接受实现了 Summary 和 Display traits 的对象作为参数
pub fn notify(item: &(impl Summary + Display)) {
    println!("Breaking news!");
    println!("Summary: {}", item.summarize());
    item.display();
}



fn main() {
    let article = CCTV {
        author: "Sports editor".to_string(),
        content: "2024欧洲杯 聚焦绿茵盛宴!".to_string(),
    };

    notify(&article);
}
  • 运行结果:
Breaking news!
Summary: 2024欧洲杯 聚焦绿茵盛宴!, by Sports editor
Author: Sports editor
Content: 2024欧洲杯 聚焦绿茵盛宴!
// 定义两个trait,一个“总结”,一个“展示”
trait Summary {
    fn summarize(&self) -> String;
}
trait Display {
    fn display(&self);
}

// 定义结构体 CCTV
struct CCTV {
    author: String,   content: String,
}

// 为CCTV实现总结功能
impl Summary for CCTV {
    fn summarize(&self) -> String {
        format!("{}, by {}", self.content, self.author)
    }
}

// 为CCTV实现展示功能
impl Display for CCTV {
    fn display(&self) {
        println!("Author: {}", self.author);
        println!("Content: {}", self.content);
    }
}

// 定义Hacknews 结构体
struct Hacknews {
    username: String,
    content: String,
}
// 为Hacknews实现“展示功能”
impl Display for Hacknews {
    fn display(&self) {
        println!("Tweet by {}: {}", self.username, self.content);
    }
}

// 定义一个函数,接受实现了 Summary 和 Display traits 的对象作为参数
pub fn notify(item: &(impl Summary + Display)) {
    println!("Breaking news!");
    println!("Summary: {}", item.summarize());
    item.display();
}

fn main() {
    let article = CCTV {
        author: "Sports editor".to_string(),
        content: "2024欧洲杯 聚焦绿茵盛宴!".to_string(),
    };

    let hack = Hacknews {
        username: "Mako".to_string(),
        content: "fast, production-grade web bundler based on Rust (makojs.dev) ! from https://makojs.dev/blog/mako-open-sourced".to_string(),
    };

    notify(&article);
    // 需要多个trait都实现了才能运行
    // notify(&hack); // 报错 the trait bound 'Tweet:summary' is not satisfied ,the trait 'summary' is implemented for 'NewsArticle'
}

泛型中使用Trait

// 定义两个Trait
trait Printable {
    fn print(&self);
}

trait Drawable {
    fn draw(&self);
}

// 实现这两个Trait的类型
struct Circle {
    radius: f64,
}

impl Printable for Circle {
    fn print(&self) {
        println!("Circle with radius {}", self.radius);
    }
}

impl Drawable for Circle {
    fn draw(&self) {
        println!("Drawing a circle with radius {}", self.radius);
    }
}

// 函数接受实现了两个Trait的类型
fn print_and_draw<T>(item: &T)
where
    T: Printable + Drawable,
{
    item.print();
    item.draw();
}

fn main() {
    let c = Circle { radius: 3.5 };

    c.print(); // 输出: Circle with radius 3.5
    c.draw();  // 输出: Drawing a circle with radius 3.5

    print_and_draw(&c);
}

trait bound形式

  • 从以上的例子中可以发现。在 Rust 中,有两种主要的方式来约束泛型类型参数必须实现特定的 trait(特性):trait bound 形式和 impl Trait 语法糖形式。让我们来详细比较它们:

  • Trait Bound 形式:Trait bound 形式使用 where 子句来为泛型类型参数指定 trait 约束。例如:

fn example<T>(item: &T) -> usize
where
    T: Display + Clone,
{
    // 函数体
}
  • 这里 T: Display + Clone 表示泛型类型 T 必须同时实现 DisplayClone 这两个 trait。关键点在于 where 子句可以将多个 trait 约束放在一起,并且在使用泛型时可以非常清晰地看到这些约束。

  • impl Trait 语法糖形式

impl Trait 语法糖形式用于在函数签名中直接指定参数必须实现的一个或多个 trait。例如:

fn notify(item: &(impl Summary + Display)) {
    // 函数体
}
  • 这里 &(impl Summary + Display) 表示 item 参数必须实现 SummaryDisplay 这两个 trait。这种写法更加简洁和紧凑,适用于函数参数较少且只有少数几个约束的情况。

Ttait作为返回类型

在这里插入图片描述
在这里插入图片描述

#[derive(Trait)]

  • 在定义一些struct往往会有#[derive(Debug)]标签。Debug trait 是 Rust 标准库提供的一个 trait,用于以调试输出的格式打印类型的实例。当一个结构体或枚举被标记为 #[derive(Debug)] 时,Rust 编译器会自动为这个类型生成实现 Debug trait 的代码。这使得我们可以通过使用 {:?} 或者 println!("{:?}", value) 等方式打印这个类型的实例。

    #[derive(Debug)]
    struct MyStruct {
        field1: i32,
        field2: String,
    }
    
    • 在这个例子中,MyStruct 结构体通过 #[derive(Debug)] 派生了 Debug trait。这样,我们就可以使用 println!("{:?}", my_struct_instance) 来打印 MyStruct 的实例。
  • 更多相关 #[derive(Trait)]的内容可去了解一下过程宏,下面是一个例子,使得在主程序中可以使用宏 AnswerFn 来自动生成一个 answer() 函数,可以按照以下步骤进行:

第一步:创建 proc-macro crate
  1. 在一个新目录中创建一个 Cargo 项目:
cargo new proc-macro-examples --lib
  1. Cargo.toml 文件中的内容更新为:
[package]
name = "proc-macro-examples"
version = "0.1.0"
edition = "2021"

[lib]
proc-macro = true

[dependencies]
# 没有依赖
  1. 更新 src/lib.rs 文件,添加 proc-macro 的实现:
#![crate_type = "proc-macro"]
extern crate proc_macro;
use proc_macro::TokenStream;

#[proc_macro_derive(AnswerFn)]
pub fn derive_answer_fn(_item: TokenStream) -> TokenStream {
    "fn answer() -> u32 { 123 }".parse().unwrap()
}
第二步:创建使用宏的应用程序

接下来,创建一个使用 proc-macro crate 的 Rust 应用程序。这个应用程序将使用宏 AnswerFn 来为一个结构体自动生成 answer() 函数。

  1. 创建另一个新的 Rust 项目:
cargo new macro-app
  1. 更新 Cargo.toml 文件以依赖于我们刚刚创建的 proc-macro crate:
[package]
name = "macro-app"
version = "0.1.0"
edition = "2021"

[dependencies]
proc-macro-examples = { path = "../proc-macro-examples" }
  1. 更新 src/main.rs 文件,使用宏 AnswerFn
extern crate proc_macro_examples;
use proc_macro_examples::AnswerFn;

#[derive(AnswerFn)]
struct Struct;

fn main() {
    assert_eq!(123, answer());
}
编译和运行
  • 先编译proc-macro-examples
cd proc-macro-examples
cargo build

然后,可以编译并运行应用程序:

cd macro-app
cargo run
  • 运行结果
PS C:\Users\kingchuxing\Documents\learning-libp2p-main\macro-app> cargo run
warning: `macro-app` (bin "macro-app") generated 1 warning
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.34s
     Running `target\debug\macro-app.exe`
PS C:\Users\kingchuxing\Documents\learning-libp2p-main\macro-app> 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1886880.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[ROS 系列学习教程] 建模与仿真 - 使用 ros_control 控制差速轮式机器人

ROS 系列学习教程(总目录) 本文目录 一、差速轮式机器人二、差速驱动机器人运动学模型三、对外接口3.1 输入接口3.2 输出接口 四、控制器参数五、配置控制器参数六、编写硬件抽象接口七、控制机器人移动八、源码 ros_control 提供了多种控制器&#xff0c;其中 diff_drive_cont…

Datawhale - 角色要素提取竞赛

文章目录 赛题要求一、赛事背景二、赛事任务三、评审规则1.平台说明2.数据说明3.评估指标4.评测及排行 四、作品提交要求五、 运行BaselineStep1&#xff1a;下载相关库Step2&#xff1a;配置导入Step3&#xff1a;模型测试Step4&#xff1a;数据读取Step5&#xff1a;Prompt设…

工业 web4.0UI 风格品质卓越

工业 web4.0UI 风格品质卓越

【力扣 - 每日一题】3115. 质数的最大距离(一次遍历、头尾遍历、空间换时间、埃式筛、欧拉筛、打表)Golang实现

原题链接 题目描述 给你一个整数数组 nums。 返回两个&#xff08;不一定不同的&#xff09;质数在 nums 中 下标 的 最大距离。 示例 1&#xff1a; 输入&#xff1a; nums [4,2,9,5,3] 输出&#xff1a; 3 解释&#xff1a; nums[1]、nums[3] 和 nums[4] 是质数。因此答…

WPF自定义模板--Button

属性&#xff1a; TemplateBinding&#xff1a;用于在ControlTemplate中绑定到控件的属性&#xff0c;例如Background、BorderBrush等。TargetType&#xff1a;指定该模板应用于哪种控件类型。在这个例子中&#xff0c;是Button。 标准的控件模板代码&#xff1a; <Style…

线性代数大题细节。

4.4 方程组解的结构&#xff08;二&#xff09;_哔哩哔哩_bilibili

eNSP中WLAN的配置和使用

一、基础配置 1.拓扑图 2.VLAN和IP配置 a.R1 <Huawei>system-view [Huawei]sysname R1 GigabitEthernet 0/0/0 [R1-GigabitEthernet0/0/0]ip address 200.200.200.200 24 b.S1 <Huawei>system-view [Huawei]sysname S1 [S1]vlan 100 [S1-vlan100]vlan 1…

vue3 window.location 获取正在访问的地址,也可以通过useRoute来获取相关信息。

1、一般我们在开发的vue3项目的时候&#xff0c;地址是这样&#xff1a;http://192.168.1.101:3100/#/login 然后我们在布署完成以后一般是这样https://xxx.yyyyy.com/uusys/#/login 其实xxx可以是www&#xff0c;也可以是一个二级域名 yyyyy.com是域名&#xff0c;uusys一般…

家政小程序的开发:打造现代式便捷家庭服务

随着现代生活节奏的加快&#xff0c;人们越来越注重生活品质与便利性。在这样的背景下&#xff0c;家政服务市场迅速崛起&#xff0c;成为许多家庭日常生活中不可或缺的一部分。然而&#xff0c;传统的家政服务往往存在信息不对称、服务效率低下等问题。为了解决这些问题&#…

Windows编程上

Windows编程[上] 一、Windows API1.控制台大小设置1.1 GetStdHandle1.2 SetConsoleWindowInfo1.3 SetConsoleScreenBufferSize1.4 SetConsoleTitle1.5 封装为Innks 2.控制台字体设置以及光标调整2.1 GetConsoleCursorInfo2.2 SetConsoleCursorPosition2.3 GetCurrentConsoleFon…

elementPlus自定义el-select下拉样式

如何在f12元素选择器上找到下拉div呢&#xff1f; 给el-select添加 :popper-append-to-body"false" 即可&#xff0c;这样就可以将下拉框添加到body元素中去&#xff0c;否则当我们失去焦点&#xff0c;下拉就消失了&#xff0c;在元素中找不到el-select。剩下就可以…

华硕魔霸5原装Windows10原厂系统 工厂模式 带ASUS Recovery恢复功能

华硕工厂文件恢复系统 &#xff0c;安装结束后带隐藏分区&#xff0c;一键恢复&#xff0c;以及机器所有驱动软件。 系统版本&#xff1a;Windows10 原厂系统下载网址&#xff1a;http://www.bioxt.cn 需准备一个20G以上u盘进行恢复 请注意&#xff1a;仅支持以上型号专用…

系统架构设计师 - 计算机网络(2)

计算机网络 计算机网络IPv6 ★概念IPv6 的优势IPv6 数据格式IPv6 地址应用IPv6 自动 IP 地址配置&#xff08;了解&#xff09;IPv4/IPv6过渡技术 网络接入&#xff08;了解&#xff09;综合布线系统 ★物联网&#xff08;了解&#xff09;概念分层 云计算&#xff08;了解&…

顺序串算法库构建

学习贺利坚老师顺序串算法库 数据结构之自建算法库——顺序串_创建顺序串s1,创建顺序串s2-CSDN博客 本人详细解析博客 串的概念及操作_串的基本操作-CSDN博客 版本更新日志 V1.0: 在贺利坚老师算法库指导下, 结合本人详细解析博客思路基础上,进行测试, 加入异常弹出信息 v1.0补…

将一个程序设置为开机启动【win11】

Windows 在 Windows 系统中&#xff0c;可以通过在 “启动” 文件夹中放置程序的快捷方式来实现开机启动。 按照以下步骤操作&#xff1a; 按 Win R 打开 “运行” 对话框&#xff0c;输入 shell:startup&#xff0c;然后按回车。这将打开 “启动” 文件夹。 找到你想设置为…

动态规划——打家劫舍(C++)

好像&#xff0c;自己读的书确实有点少了。 ——2024年7月2日 198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; 题目描述 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连…

Python特征工程 — 1.2 特征分箱

目录 1 什么是特征分箱 2 分箱的重要性及其优势 3 有监督分箱 3.1卡方分箱原理 3.2 决策树分箱 4 无监督分箱 4.1 等距分箱 4.2 等频分箱 4.3 分位数分箱 实验数据&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1yT1ct_ZM5uFLgcYsaBxnHg?pwdczum 提取码&…

JAVA期末速成库(11)第十二章

一、习题介绍 第十二章 Check Point&#xff1a;P454 12.1&#xff0c;12.9&#xff0c;12.10&#xff0c;12,12 二、习题及答案 12.1 What is the advantage of using exception handling? 12.1使用异常处理的优势是什么? 答:使用异常处理有以下优势&#xff1a; 1. 提高…

浙江建筑安全员A证2024年最新考试题库练习

46.总承包单位依法将建设工程分包给其他单位的&#xff0c;分包合同中应当明确各自的安全生产方面的权利、义务。总承包单位对分包工程的安全生产承担&#xff08;&#xff09;责任。 A.全部 B.主要 C.部分 D.连带 答案&#xff1a;D 47.实施总承报的建设工程发生事故&…

一首歌的时间 写成永远

大家好&#xff0c;我是秋意零。 就在&#xff0c;2024年6月20日。我本科毕业了&#xff0c;之前专科毕业挺有感触&#xff0c;也写了一篇文章进行记录。如今又毕业了&#xff0c;还是写一篇文章记录吧&#xff01;&#xff01; 专科毕业总结&#xff1a;大学三年总结&#xf…