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

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)
whereT: Printable + Drawable,
{item.print();item.draw();
}fn main() {let c = Circle { radius: 3.5 };c.print(); // 输出: Circle with radius 3.5c.draw();  // 输出: Drawing a circle with radius 3.5print_and_draw(&c);
}

trait bound形式

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

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

fn example<T>(item: &T) -> usize
whereT: 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 warningFinished `dev` profile [unoptimized + debuginfo] target(s) in 0.34sRunning `target\debug\macro-app.exe`
PS C:\Users\kingchuxing\Documents\learning-libp2p-main\macro-app> 

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

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

相关文章

【C++】#1

关键字&#xff1a; 基本框架、多个main执行、快捷键、cout规则 基本框架&#xff1a; #include <iostream> using namespace std;int main() {//具体内容return 0; } 多个main函数可执行&#xff1a; 常用快捷键&#xff1a; cout规则&#xff1a;

Qt中文乱码如何解决

目录 一、使用建议 二、其它设置 一、使用建议 Qt对中文的支持不是很友好&#xff0c;使用QtCreator会出现各种乱七八糟的中文代码问题&#xff0c;如何处理这种问题&#xff1f; &#xff08;1&#xff09;粘贴别人的代码时&#xff0c;先在记事本里粘贴一遍&#xff0c;再…

信号与系统-实验5 离散时间系统的时域分析

一、实验目的 1、理解离散信号的定义与时域特征&#xff0c;掌握在时域求解信号的各种变换运算&#xff1b; 2、掌握离散系统的单位响应及其 MATLAB 实现的方法&#xff1b; 3、掌握离散时间序列卷积及其 MATLAB 实现的方法&#xff1b; 4、掌握利用 MATLAB 求解微分方程&a…

Android隐藏状态栏和修改状态栏颜色_亲测有效

本文记录了隐藏状态栏和修改状态栏颜色以及电量、WiFi标志等内容的模式显示&#xff0c;亲测有效。 1、隐藏屏幕状态栏 public void hideStatusBar(BaseActivity activity) {Window window activity.getWindow();//没有这一行无效window.addFlags(WindowManager.LayoutParam…

新手教学系列——慎用Flask-SQLAlchemy慢日志记录

在使用 Flask-SQLAlchemy 开发应用时&#xff0c;了解和避免潜在的问题是非常重要的。特别是在常驻进程和循环执行任务的场景下&#xff0c;慢查询记录功能&#xff08;SQLALCHEMYRECORDQUERIES&#xff09;可能会引发严重的内存泄漏问题。本文将详细介绍这个问题&#xff0c;并…

博通 VMware 不再支持中文?到底还有哪款虚拟机值得一用?

号主&#xff1a;老杨丨11年资深网络工程师&#xff0c;更多网工提升干货&#xff0c;请关注公众号&#xff1a;网络工程师俱乐部 下午好啊&#xff0c;我的网工朋友。 说起虚拟机&#xff0c;VMware绝对是永远的一哥。 但VMware最近真的“消息”不断啊…… 就上个月&#x…

【Python机器学习】算法链与管道——用预处理进行参数选择的注意项

对于许多机器学习算法&#xff0c;提供的特定数据表示非常重要。比如&#xff0c;首先对数据进行缩放&#xff0c;然后手动合并特征&#xff0c;再利用无监督机器学习来学习特征。因此&#xff0c;大多数机器学习应用不仅需要应用多个算法&#xff0c;而且还需要将许多不同的处…

ByteTrack论文阅读笔记

目录 ByteTrack: Multi-Object Tracking by Associating Every Detection Box摘要INTRODUCTION — 简介BYTE算法BYTE算法用Python代码实现实验评测指标轻量模型的跟踪性能 总结SORT算法简介ByteTrack算法和SORT算法的区别 ByteTrack: Multi-Object Tracking by Associating Eve…

QT加载安装外围依赖库的翻译文件后翻译失败的现象分析:依赖库以饿汉式的形式暴露单例接口导致该现象的产生

1、前提说明 VS2019 QtClassLibaryDll是动态库,QtWidgetsApplication4是应用程序。 首先明确:动态库以饿汉式的形式进行单例接口暴露; 然后,应用程序加载动态库的翻译文件并进行全局安装; // ...QTranslator* trans = new QTranslator();//qDebug() << trans->…

暑期大数据人工智能学习-企业项目试岗实训开营

暑期企业项目-试岗实训活动全面开启啦 跟张良均老师学大数据人工智能 不仅可以提供实习证明&#xff0c;有需要话也可以提供实习鉴定报告 √54个热门案例拆解 √40项目实战课程 √27个项目可选 √4个项目方向

居然这么简单就能实现扫雷游戏!

目录 一.思路 1.成果展示 2.思路 二.具体操作 1.创建"棋盘" 2.初始化雷 3.布置雷 4.打印 5.排除雷 三.代码实现 1.test.c文件 2.thunder.h文件 3.thunder.c文件 Hello&#xff0c;大家好&#xff0c;今天我们来实现扫雷游戏&#xff0c;希望这一篇博客能给带给大家一…

使用LabVIEW报告生成工具包时报错97

问题详情&#xff1a; 在运行使用Excel/Word调用节点的程序时&#xff0c;收到错误97&#xff1a;LabVIEW&#xff1a;&#xff08;十六进制0x61&#xff09;输入中传递了一个空引用句柄或先前已删除的引用句柄。 当运行报告生成工具包中的一个示例程序时&#xff0c;收到错误…

速锐得解码汽车以太网技术特点接口定义数据传输及应用

在当前的汽车工业中&#xff0c;随着技术的飞速发展&#xff0c;车载网络技术也在不断进步与更新。其中&#xff0c;具备以太网的车型已成为一个新兴趋势&#xff0c;这主要归功于车载以太网技术在车内带宽需求较高的系统上的应用&#xff0c;如高级驾驶辅助系统&#xff08;AD…

1000T的文件怎么能快速从南京传到北京?最佳方案你肯定想不到

今天刷面试题看到一个有意思的面试题&#xff0c; 1000T的文件怎么能以最快速度从南京传到北京&#xff1f; 网络传输 首先我们考虑通过网络传输&#xff0c;需要多长时间。 我特地咨询了在运营商工作的同学&#xff0c;目前带宽&#xff1a; 家庭宽带下行最大1Gbps&#…

Linux应用---内存映射

写在前面&#xff1a; 在进程间通信中&#xff0c;有一种方式内存映射。内存映射也是进程间通信的方式之一&#xff0c;其效率高&#xff0c;可以直接对内存进行操作。本节我们对内存映射进行学习&#xff0c;并结合案例进行实践。 1、基本理论 内存映射&#xff1a;是将磁盘文…

代码随想录-Day46

121. 买卖股票的最佳时机 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从…

【JVM-05】Java内存区域(运行时数据区)、对象创建过程、内存布局

【JVM-05】Java内存区域即运行时数据区、对象创建过程、内存布局 1. 介绍下Java内存区域(运行时数据区)1.1 程序计数器(线程私有)1.2 虚拟机栈(线程私有)1.3 本地方法栈(线程私有)1.4 Java堆(线程共享)1.5 方法区(线程共享)1.5.1 方法区和永久代的关系1.5.2 常用参数1.5.3 为什…

Halcon支持向量机

一 支持向量机 1 支持向量机介绍&#xff1a; 支持向量机(Support Vector Machine&#xff0c;SVM)是Corinna Cortes和Vapnik于1995年首先提出的&#xff0c;它在解决小样本、非线性及高维模式识别表现出许多特有的优势。 2 支持向量机原理: 在n维空间中找到一个分类超平面…

node与npm安装教程

node与npm的下载安装教程&#xff1a; 文章目录 node与npm的下载安装教程&#xff1a;---Node.js 介绍NPM 介绍 一&#xff1a;下载&#xff08;node与npm的安装包是在一起的&#xff09;二&#xff1a;安装1&#xff1a;双击运行安装文件1-node-v14.15.0-x64.msi,点击下一步。…

常用渲染管线介绍

1、光栅化渲染管线&#xff08;Raster pipeline&#xff09; 1.1、光栅化概述 光栅化图形渲染管线是实时渲染的核心组件。渲染管线的功能是通过给定虚拟相机、3D场景物体以及光源等场景要素来产生或者渲染一副2D的图像。如上图所示&#xff0c;场景中的3D物体通过管线转变为屏…