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)
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.mfbz.cn/a/764996.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深层神经网络

深层神经网络 深层神经网络 深度神经网络&#xff08;Deep Neural Networks&#xff0c;DNN&#xff09;可以理解为有很多隐藏层的神经网络&#xff0c;又被称为深度前馈网络&#xff08;DFN&#xff09;&#xff0c;多层感知机&#xff08;Multi-Layer perceptron&#xff0c…

音视频同步的关键:深入解析PTS和DTS

&#x1f60e; 作者介绍&#xff1a;我是程序员行者孙&#xff0c;一个热爱分享技术的制能工人。计算机本硕&#xff0c;人工制能研究生。公众号&#xff1a;AI Sun&#xff0c;视频号&#xff1a;AI-行者Sun &#x1f388; 本文专栏&#xff1a;本文收录于《音视频》系列专栏&…

【ES】--Elasticsearch的Nested类型介绍

目录 一、问题现象二、普通数组类型1、为什么普通数组类型匹配不准?三、nested类型四、nested类型查询操作1、只根据nested对象内部数组条件查询2、只根据nested对象外部条件查询3、根据nested对象内部及外部条件查询4、向nested对象数组追加新数据5、删除nested对象数组某一个…

Python+Pytest+Allure+Yaml+Pymysql+Jenkins+GitLab接口自动化测试框架详解

PythonPytestAllureYaml接口自动化测试框架详解 编撰人&#xff1a;CesareCheung 更新时间&#xff1a;2024.06.20 一、技术栈 PythonPytestAllureYamlJenkinsGitLab 版本要求&#xff1a;Python3.7.0,Pytest7.4.4,Allure2.18.1,PyYaml6.0 二、环境配置 安装python3.7&…

Windows下快速安装Open3D-0.18.0(python版本)详细教程

目录 一、Open3D简介 1.1主要用途 1.2应用领域 二、安装Open3D 2.1 激活环境 2.2 安装open3d 2.3测试安装是否成功 三、测试代码 3.1 代码 3.2 显示效果 一、Open3D简介 Open3D 是一个强大的开源库&#xff0c;专门用于处理和可视化3D数据&#xff0c;如点云、网格和…

linux内核驱动第一课(基于RK3568)

学习Linux驱动需要以下基础知识&#xff1a; C语言编程&#xff1a;掌握C语言是开发Linux驱动程序的基本要求。操作系统原理&#xff1a;了解操作系统的基本概念和原理&#xff0c;如进程管理、内存管理、中断处理等。Linux内核&#xff1a;熟悉Linux内核的结构和工作机制&…

编译libvlccpp

首先下载vlc sdk https://get.videolan.org/vlc/3.0.9.2/win64/vlc-3.0.9.2-win64.7z Cmake 生成libvlccpp vs2022工程文件 编译libvlccpp 编译出错需修改代码 错误信息&#xff1a; \VLC\sdk\include\vlc/libvlc_media.h(368): error C2065: “libvlc_media_read_cb”: 未…

Python程序语法元素简析

文章目录 Python程序的语法元素是构成Python程序的基础构建块&#xff0c;它们共同决定了程序的结构、逻辑和行为。以下是一些关键的Python语法元素简析&#xff1a; 注释&#xff1a;用于解释代码功能&#xff0c;不被执行。单行注释以#开始&#xff0c;多行注释使用三个单引号…

智能写作与痕迹消除:AI在创意文案和论文去痕中的应用

作为一名AI爱好者&#xff0c;我积累了许多实用的AI生成工具。今天&#xff0c;我想分享一些我经常使用的工具&#xff0c;这些工具不仅能帮助提升工作效率&#xff0c;还能激发创意思维。 我们都知道&#xff0c;随着技术的进步&#xff0c;AI生成工具已经变得越来越智能&…

怎样恢复数据?电脑数据恢复方法详解!

在日常使用电脑或移动设备时&#xff0c;我们难免会遇到数据丢失的情况&#xff0c;如误删除文件、存储设备故障等。数据恢复成了许多人迫切需要解决的问题。本文将为您介绍几种高效的数据恢复方法&#xff0c;帮助您轻松找回丢失的文件。 一、了解数据丢失的原因 在恢复数据…

Centos安装1Panel面板工具安装可视化界面

1Panel是一种市场调研平台&#xff0c;旨在帮助企业进行市场研究和获取消费者反馈。它通过在线调查和观察研究的方式&#xff0c;帮助企业了解他们的目标市场&#xff0c;并针对市场需求做出相应的决策。 1Panel的特点包括&#xff1a; 1. 全球范围&#xff1a;1Panel在全球范…

学习笔记(linux高级编程)10

IPC 进程间通信 interprocess communicate 三大类&#xff1a; 1、古老的通信方式 无名管道 有名管道 信号 2、IPC对象通信 system v BSD suse fedora kernel.org 消息队列(用的相对少&#xff0c;这里不讨论) 共享内存 信号量集 3、socket通信 网络通信 特…

Linux登录界面

Linux登录界面 1. 起因2. 脚本3. 效果 1. 起因 某次刷抖音看到一个博主展示了一个登录页面,觉得蛮好看的.于是自己动手也写一个 2. 脚本 编写脚本/usr/local/bin/login.sh #!/bin/bash Current_timedate %Y-%m-%d %H:%M:%S Versioncat /etc/redhat-release Kernel_Version…

合作协议的网络接入协议

合作协议的网络接入协议 介绍阿里云获取网络接入协议查看合同生成新合同总结 介绍 最近在帮公司弄增值电信业务经营许可证的相关的材料&#xff0c;然后需要我提供网络接入商的网络接入协议。因为每个公司买的服务器可能都不一样&#xff0c;有的阿里云、华为云、腾讯云等还有…

币界网讯,币安准备与SEC 展开长期法律对决

刚刚&#xff0c;数字货币交易所的领头羊Binance公布了法律策略&#xff0c;未来将会采取大胆举措与美国证券交易委员会 (SEC) 展开长期法律斗争&#xff0c;彰显其对监管合规的承诺。小编认为&#xff0c;Binance的这一战略立场是向美国SEC传递的道歉信&#xff0c;自从美国SE…

【0299】Postgres内核之哈希表(Hash Tables)

0. 哈希表(Hash Tables) 哈希表是 一种用于存储键值对的数据结构。与使用索引号访问元素的基本数组不同,哈希表使用键来查找表条目。这使得数据管理对于用户来说更易于管理,因为按属性对数据条目进行分类比按它们在一个巨大的列表中的数量更容易。 在 C++ 中,我们将哈希…

MySQL自学教程:1. MySQL简介与安装

MySQL简介与安装 一、MySQL简介二、MySQL安装(一)Windows系统上的安装(二)Linux系统上的安装(以Ubuntu为例)(三)Mac OS系统上的安装三、安装后的基本配置四、总结一、MySQL简介 MySQL是一个流行的开源关系型数据库管理系统(RDBMS),广泛应用于各种业务场景,从小型个…

干货分享:代理IP的10大误区

在当今的数字时代&#xff0c;代理已成为在线环境不可或缺的一部分。它们的用途广泛&#xff0c;从增强在线隐私到绕过地理限制。然而&#xff0c;尽管代理无处不在&#xff0c;但仍存在许多围绕代理的误解。在本博客中&#xff0c;我们将探讨和消除一些最常见的代理误解&#…

AI太火,今年更缺人了 (含实习)

AI太火了&#xff01;眼睛一睁一闭&#xff0c;一大堆新鲜出炉的前沿科技进展已经塞满未读列表。 许多公司更是开出了高薪&#xff0c; 读者福利&#xff1a;如果大家对大模型感兴趣&#xff0c;这套大模型学习资料一定对你有用 对于0基础小白入门&#xff1a; 如果你是零基础…

构建LangChain应用程序的示例代码:50、如何在检索-生成 (RAG) 应用中利用多模态大型语言模型 (LLM) 处理包含文本和图像的混合文档的示例

多模态 RAG 许多文档包含多种内容类型&#xff0c;包括文本和图像。 然而&#xff0c;大多数 RAG 应用中&#xff0c;图像中捕获的信息往往被忽略。 随着多模态 LLM 的出现&#xff0c;如 GPT-4V&#xff0c;值得考虑如何在 RAG 中利用图像&#xff1a; 选项 1&#xff1a;…