当前位置: 首页 > news >正文

Rust-Trait 特征编程

昨夜江边春水生,艨艟巨舰一毛轻。

向来枉费推移力,此日中流自在行。

——《活水亭观书有感二首·其二》宋·朱熹

【哲理】往日舟大水浅,众人使劲推船,也是白费力气,而此时春水猛涨,巨舰却自由自在地飘行在水流中。

君子谋时而动,顺势而为。借助客观的事物之后,以往很难的事情也会变得简单。

一、Trait 特征介绍

在 Rust 编程语言中,特征(Traits)是一种定义共享行为的机制。它们类似于其他编程语言中的接口(Interfaces),用于描述一组方法,这些方法可以由不同类型实现。通过使用特征,可以定义某些类型必须提供的方法,从而实现多态和代码复用。

二、特征的定义

特征使用 trait 关键字来定义。以下是一个简单的特征示例:

trait Summary {fn summarize(&self) -> String;
}

在这个例子中,Summary 特征定义了一个名为 summarize 的方法,该方法接受一个不可变引用 &self 并返回一个 String

三、实现特征

要让一个类型实现某个特征,需要使用 impl 关键字。以下是一个结构体及其对 Summary 特征的实现:

struct Article {title: String,content: String,
}impl Summary for Article {fn summarize(&self) -> String {format!("{}: {}", self.title, self.content)}
}

在这个例子中,Article 结构体实现了 Summary 特征,并提供了 summarize 方法的具体实现。

四、使用特征

实现了特征的类型可以通过特征的方法进行调用:

fn main() {let article = Article {title: String::from("Rust Programming"),content: String::from("Rust is a systems programming language."),};println!("{}", article.summarize());
}

五、默认方法

特征还可以提供默认方法实现。如果某个类型没有提供特定方法的实现,则会使用默认实现:

trait Summary {fn summarize(&self) -> String {String::from("(Read more...)")}
}

类型仍然可以选择覆盖默认实现:

impl Summary for Article {fn summarize(&self) -> String {format!("{}: {}", self.title, self.content)}
}

六、特征约束

特征可以作为泛型函数或类型的约束条件,以确保类型实现了特定的特征:

fn notify(item: &impl Summary) {println!("Breaking news! {}", item.summarize());
}

或者使用更灵活的语法:

fn notify<T: Summary>(item: &T) {println!("Breaking news! {}", item.summarize());
}

七、关联类型和特征

特征还可以包含关联类型,用于定义与特征相关的类型占位符:

trait Iterator {type Item;fn next(&mut self) -> Option<Self::Item>;
}

实现特征时需要指定关联类型:

struct Counter;impl Iterator for Counter {type Item = u32;fn next(&mut self) -> Option<Self::Item> {// Implementation goes hereSome(0)}
}

八、静态派发与动态派发

1、静态派发(Static Dispatch)

定义:静态派发是在编译时确定具体调用哪个方法。编译器会生成特定类型的代码,并在编译时将方法调用绑定到具体的实现上。

实现方式:在 Rust 中,静态派发通常通过泛型和特征约束来实现。例如:

fn draw<T: Draw>(component: &T) {component.draw();
}

性能

  • 由于方法调用在编译时已经确定,静态派发没有运行时开销,因此性能更高。
  • 编译器可以进行内联优化,从而进一步提升性能。

代码大小

  • 静态派发可能会导致代码膨胀,因为每个具体类型都会生成一份独立的代码。

灵活性

  • 静态派发要求在编译时知道所有类型,因此在某些情况下灵活性较差。

2、动态派发(Dynamic Dispatch)

定义:动态派发是在运行时决定具体调用哪个方法。通过特征对象(Trait Objects),Rust 可以在运行时查找并调用具体类型的方法。

实现方式:在 Rust 中,动态派发通常通过特征对象来实现。例如:

fn draw(component: &dyn Draw) {component.draw();
}

性能

  • 动态派发有一定的运行时开销,因为需要在运行时查找方法并进行调用。
  • 没有编译时的内联优化。

代码大小

  • 动态派发不会导致代码膨胀,因为所有类型共享相同的调用路径。

灵活性

  • 动态派发允许在运行时处理不同类型,因此更加灵活,适用于需要处理多态行为的场景。

3、示例对比

静态派发示例

trait Draw {fn draw(&self);
}struct Button {label: String,
}impl Draw for Button {fn draw(&self) {println!("Drawing a button with label: {}", self.label);}
}struct TextField {text: String,
}impl Draw for TextField {fn draw(&self) {println!("Drawing a text field with text: {}", self.text);}
}fn draw_static<T: Draw>(component: &T) {component.draw();
}fn main() {let button = Button {label: String::from("Submit"),};let text_field = TextField {text: String::from("Enter your name"),};draw_static(&button);draw_static(&text_field);
}

动态派发示例

trait Draw {fn draw(&self);
}struct Button {label: String,
}impl Draw for Button {fn draw(&self) {println!("Drawing a button with label: {}", self.label);}
}struct TextField {text: String,
}impl Draw for TextField {fn draw(&self) {println!("Drawing a text field with text: {}", self.text);}
}fn draw_dynamic(component: &dyn Draw) {component.draw();
}fn main() {let button = Button {label: String::from("Submit"),};let text_field = TextField {text: String::from("Enter your name"),};let components: Vec<&dyn Draw> = vec![&button, &text_field];for component in components {draw_dynamic(component);}
}

4、总结

  • 静态派发:在编译时确定方法调用,性能更高,但灵活性较低,适用于类型已知且不频繁变化的场景。
  • 动态派发:在运行时确定方法调用,灵活性更高,但有一定的性能开销,适用于需要处理多态行为的场景。

选择使用哪种派发方式取决于具体应用场景的需求和性能考虑。

九、与 Java 比对

Rust 中的特征(Traits)和 Java 中的接口(Interfaces)有很多相似之处,但也存在一些关键的区别。以下是对比总结:

1、相似点

  1. 定义行为

    • Rust Traits:用于定义一组方法签名,这些方法可以在实现该特征的类型上调用。
    • Java Interfaces:用于定义一组方法签名,这些方法可以在实现该接口的类上调用。
  2. 多重实现

    • Rust Traits:一个类型可以实现多个特征。
    • Java Interfaces:一个类可以实现多个接口。
  3. 抽象方法

    • Rust Traits:可以包含没有默认实现的方法,这些方法必须由实现特征的类型来实现。
    • Java Interfaces:可以包含抽象方法,这些方法必须由实现接口的类来实现。

2、不同点

  1. 默认实现

    • Rust Traits:允许为方法提供默认实现。如果类型不提供自己的实现,则使用默认实现。
    • Java Interfaces:从 Java 8 开始,接口可以包含默认方法(default methods),但这在历史上并不是一直存在的。
  2. 关联类型与泛型

    • Rust Traits:支持关联类型(associated types)和泛型参数,使得特征更加灵活。例如,可以定义一个特征 Iterator,它有一个关联类型 Item
    • Java Interfaces:主要通过泛型来实现类似的功能,但没有直接的关联类型概念。
  3. 静态分发与动态分发

    • Rust Traits:默认情况下,特征方法调用是静态分发的(即在编译时确定)。可以通过特征对象(trait objects)实现动态分发(即运行时确定)。
    • Java Interfaces:接口方法调用通常是动态分发的(即通过虚方法表在运行时确定)。
  4. 所有权与生命周期

    • Rust Traits:由于 Rust 的所有权系统,特征实现中需要考虑生命周期(lifetimes)和借用检查器(borrow checker)。
    • Java Interfaces:Java 有垃圾回收机制,不需要显式管理内存和生命周期。

3、示例代码

Rust Traits 示例

// 定义一个特征
trait Drawable {fn draw(&self);
}// 实现特征
struct Circle;
impl Drawable for Circle {fn draw(&self) {println!("Drawing a circle");}
}struct Square;
impl Drawable for Square {fn draw(&self) {println!("Drawing a square");}
}fn main() {let shapes: Vec<Box<dyn Drawable>> = vec![Box::new(Circle), Box::new(Square)];for shape in shapes {shape.draw();}
}

Java Interfaces 示例

// 定义一个接口
interface Drawable {void draw();
}// 实现接口
class Circle implements Drawable {public void draw() {System.out.println("Drawing a circle");}
}class Square implements Drawable {public void draw() {System.out.println("Drawing a square");}
}public class Main {public static void main(String[] args) {Drawable[] shapes = { new Circle(), new Square() };for (Drawable shape : shapes) {shape.draw();}}
}

总的来说,Rust 的特征和 Java 的接口在概念上非常相似,但由于语言设计和特性上的差异,它们在具体实现和使用上也有不同。

参考资料

https://course.rs/basic/trait/trait.html

相关文章:

Rust-Trait 特征编程

昨夜江边春水生&#xff0c;艨艟巨舰一毛轻。 向来枉费推移力&#xff0c;此日中流自在行。 ——《活水亭观书有感二首其二》宋朱熹 【哲理】往日舟大水浅&#xff0c;众人使劲推船&#xff0c;也是白费力气&#xff0c;而此时春水猛涨&#xff0c;巨舰却自由自在地飘行在水流中…...

彻底理解哈希表(HashTable)结构

目录 介绍优缺点概念哈希函数快速的计算键类型键转索引霍纳法则 均匀的分布 哈希冲突链地址法开放地址法线性探测二次探测再哈希法 扩容/缩容实现哈希创建哈希表质数判断哈希函数插入&修改获取数据删除数据扩容/缩容函数全部代码 哈希表&#xff08;Hash Table&#xff09;…...

微信小程序的汽车维修预约管理系统

文章目录 项目介绍具体实现截图技术介绍mvc设计模式小程序框架以及目录结构介绍错误处理和异常处理java类核心代码部分展示详细视频演示源码获取 项目介绍 系统功能简述 前台用于实现用户在页面上的各种操作&#xff0c;同时在个人中心显示各种操作所产生的记录&#xff1a;后…...

LeetCode:3255. 长度为 K 的子数组的能量值 II(模拟 Java)

目录 3255. 长度为 K 的子数组的能量值 II 题目描述&#xff1a; 实现代码与解析&#xff1a; 模拟 原理思路&#xff1a; 3255. 长度为 K 的子数组的能量值 II 题目描述&#xff1a; 给你一个长度为 n 的整数数组 nums 和一个正整数 k 。 一个数组的 能量值 定义为&am…...

深入了解逻辑回归:机器学习中的经典算法

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...

软件测试基础十三(python 函数)

函数 1. 函数的意义 代码复用 提高效率&#xff1a;Python中的函数允许将一段可重复使用的代码封装起来。例如&#xff0c;在一个数据分析项目中&#xff0c;可能需要多次计算一组数据的平均值。可以将计算平均值的代码定义为一个函数&#xff1a; def calculate_average(nu…...

计算机网络——HTTP篇

基础篇 IOS七层网络模型 TCP/IP四层模型&#xff1f; 应⽤层&#xff1a;位于传输层之上&#xff0c;主要提供两个终端设备上的应⽤程序之间的通信&#xff0c;它定义了信息交换的格式&#xff0c;消息会交给下⼀层传输层来传输。 传输层的主要任务就是负责向两台设备进程之间…...

信息化运维方案,实施方案,开发方案,信息中心安全运维资料(软件资料word)

1 编制目的 2 系统运行维护 2.1 系统运维内容 2.2 日常运行维护方案 2.2.1 日常巡检 2.2.2 状态监控 2.2.3 系统优化 2.2.4 软件系统问题处理及升级 2.2.5 系统数据库管理维护 2.2.6 灾难恢复 2.3 应急运行维护方案 2.3.1 启动应急流程 2.3.2 成立应急小组 2.3.3 应急处理过程 …...

自动化工具 Gulp

自动化工具 gulp 摘要 概念&#xff1a;gulp用于自动化开发流程。 理解&#xff1a;我们只需要编写任务&#xff0c;然后gulp帮我们执行 核心概念&#xff1a; 任务&#xff1a;通过定义不同的任务来组织你的构建流程。 管道&#xff1a;通过管道方式将文件从一个插件传递…...

css实现div被图片撑开

固定好盒子的宽度&#xff0c;高度随传过来的图片大小决定 <div class"tab-con"> <img:src"concertInfo.detail"alt""> </div>.tab-con {margin-bottom: 20px;width: 700px;img {width: 700px;height: auto;object-fit: cont…...

Power Pivot、Power BI 和 SQL Server Analysis Services 的公式语言:DAX(数据分析表达式)

DAX&#xff08;Data Analysis Expressions&#xff09;是一种用于 Power Pivot、Power BI 和 SQL Server Analysis Services 的公式语言&#xff0c;旨在帮助用户进行数据建模和复杂计算。DAX 的设计初衷是使数据分析变得简单而高效&#xff0c;特别是在处理数据模型中的表关系…...

大模型应用编排工具Dify二开之工具和模型页面改造

1.前言 简要介绍下 dify&#xff1a; ​ 一款可以对接市面上主流大模型的任务编排工具&#xff0c;可以通过拖拽形式进行编排形成解决某些业务场景的大模型应用。 背景信息&#xff1a; ​ 环境&#xff1a;dify-0.8.3、docker-21 ​ 最近笔者在做 dify的私有化部署和二次…...

Pytorch用BERT对CoLA、新闻组文本数据集自然语言处理NLP:主题分类建模微调可视化分析...

原文链接&#xff1a;https://tecdat.cn/?p38181 自然语言处理&#xff08;NLP&#xff09;领域在近年来发展迅猛&#xff0c;尤其是预训练模型的出现带来了重大变革。其中&#xff0c;BERT 模型凭借其卓越性能备受瞩目。然而&#xff0c;对于许多研究者而言&#xff0c;如何高…...

LightGBM-GPU不能装在WSL,能装在windows上

这是一篇经验总结文章&#xff0c;注重思路&#xff0c;忽略细节。 1.起因 用多个机器学习方法训练模型&#xff0c;比较性能&#xff0c;发现Light GBM方法获得的性能明显更高&#xff0c;但问题是在CPU上训练的速度特别特别慢&#xff0c;需要用GPU训练。 2.开始装LightGB…...

工业相机常用功能之白平衡及C++代码分享

目录 1、白平衡的概念解析 2、相机白平衡参数及操作 2.1 相机白平衡参数 2.2 自动白平衡操作 2.3 手动白平衡操作流程 3、C++ 代码从XML读取参数及设置相机参数 3.1 读取XML 3.2 C++代码,从XML读取参数 3.3 给相机设置参数 1、白平衡的概念解析 白平衡(White Balance)…...

Foundry 单元测试

安装 Foundry 如果你还没有安装 Foundry&#xff0c;请按照此处的说明进行操作&#xff1a;Foundry 安装 Foundry Hello World 只需运行以下命令&#xff0c;它将为你设置环境&#xff0c;创建测试并运行它们。&#xff08;当然&#xff0c;这假设你已经安装了 Foundry&…...

idea database连接数据库后看不到表解决方法、格式化sql快捷键

最下面那个勾选上就可以了 或 格式化sql快捷键&#xff1a; 先选中&#xff0c; 使用快捷键格式化 SQL&#xff1a; Windows/Linux: Ctrl Alt L macOS: Cmd Alt L...

【数学二】线性代数-向量-向量组的秩、矩阵得秩

考试要求 1、理解 n n n维向量、向量的线性组合与线性表示的概念. 2、理解向量组线性相关、线性无关的概念,掌握向量组线性相关、线性无关的有关性质及判别法. 3、了解向量组的极大线性无关组和向量组的秩的概念,会求向量组的极大线性无关组及秩. 4、了解向量组等价的概念,…...

ABAP开发-内存管理

系列文章目录 文章目录 系列文章目录前言一、概述二、程序间调用三、外部会话和内部会话四、SAP内存与ABAP内存五、实例总结 前言 一、概述 内存是程序之间为了传递数据而使用的共享存储空间&#xff0c;在每个程序里使用的内存有SAP内存和ABAP内存 SAP内存分类 SAP内存 主会…...

【Ajax】跨域

文章目录 1 同源策略1.1 index.html1.2 server.js 2 如何解决跨域2.1 JSONP2.1.1 JSONP 原理2.1.2 JSONP 实践2.1.3 jQuery 中的 JSONP 2.2 CORS2.2.1 CORS实践 3 server.js 1 同源策略 同源策略(Same-Origin Policy)最早由 Netscape 公司提出&#xff0c;是浏览器的一种安全策…...

yii 常用一些调用

yii 常用一些调用 (增加中) 调用YII框架中 jquery&#xff1a;Yii::app()->clientScript->registerCoreScript(‘jquery’); framework/web/js/source的js,其中registerCoreScript key调用的文件在framework/web/js/packages.php列表中可以查看 在view中得到当前contro…...

网页版五子棋——用户模块(服务器开发)

前一篇文章&#xff1a;网页版五子棋—— WebSocket 协议-CSDN博客 目录 前言 一、编写数据库代码 1.数据库设计 2.配置 MyBatis 3.创建实体类 4.创建 UserMapper 二、前后端交互接口 1.登录接口 2.注册接口 3.获取用户信息 三、服务器开发 1.代码编写 2.测试后端…...

以RK3568为例,ARM核心板如何实现NTP精准时间同步?

背景 网络时间协议NTP&#xff08;Network TimeProtocol&#xff09;是用于互联网中时间同步的标准互联网协议&#xff0c;可以把计算机的时间同步到某些时间标准。NTP对于我们产品来说有什么用呢&#xff0c;简单的讲&#xff0c;当你的设备时间不准确了&#xff0c;你可以接…...

Twitter(X)2024最新注册教程

Twitter 现名为X&#xff0c;因为图标是一只小鸟的形象&#xff0c;大家也叫它小蓝鸟&#xff08;埃隆马斯克于 2023 年对该平台进行了品牌重塑&#xff09;&#xff0c;目前仍然是全球最受欢迎的社交媒体和微博平台之一&#xff0c;全球活跃用户量大概在4.5亿。尤其是欧美国家…...

10.桥接模式设计思想

10.桥接模式设计思想 目录介绍 01.桥接模式基础 1.1 桥接模式由来1.2 桥接模式定义1.3 桥接模式场景1.4 桥接模式思考1.5 解决的问题 02.桥接模式实现 2.1 罗列一个场景2.2 桥接结构2.3 桥接基本实现2.4 有哪些注意点 03.桥接实例演示 3.1 需求分析3.2 代码案例实现3.3 是否可…...

Java多线程详解⑤(全程干货!!!)线程安全问题 || 锁 || synchronized

这里是Themberfue 在上一节的最后&#xff0c;我们讨论两个线程同时对一个变量累加所产生的现象 在这一节中&#xff0c;我们将更加详细地解释这个现象背后发生的原因以及该如何解决这样类似的现象 线程安全问题 public class Demo15 {private static int count 0;public …...

(已解决)Dependency “ ” not found 细谈

剖析原因&#xff1a;依赖在pom文件中引用后&#xff0c;然后ReLoad&#xff0c;此依赖会在你配置的本地仓库里面找&#xff0c;并下载下来&#xff0c;他报not found就是没有找到。 本地仓库的位置&#xff1a; 进一步深究&#xff1a;在本地仓库找的时候&#xff0c;他又会…...

网络编程、UDP、TCP、三次握手、四次挥手

一、初识网络编程 网络编程的概念&#xff1a;在网络通信协议下&#xff0c;不同计算机上运行的程序&#xff0c;进行的数据传输。 应用场景&#xff1a;即时通信、网游对战、金融证券、国际贸易、邮件等等。 不管是什么场景&#xff0c;都是计算机和计算机之间通过网络进行…...

程序员的生活周刊 #7:耐克总裁被裁记

0. 庙宇 这张图来自 Tianshu Liu&#xff0c; 被树木环绕的宝塔庙宇 1. 耐克总裁 耐克最近的总裁 John Donahoe 干了 5 年&#xff0c;终于被裁掉了。 这位总裁即不了解球鞋文化&#xff0c;也没有零售经验&#xff0c;但不懂事的董事会还是聘用它&#xff0c;寄托把耐克从运…...

sparkSQL的UDF,最常用的regeister方式自定义函数和udf注册方式定义UDF函数 (详细讲解)

- UDF&#xff1a;一对一的函数【User Defined Functions】 - substr、split、concat、instr、length、from_unixtime - UDAF&#xff1a;多对一的函数【User Defined Aggregation Functions】 聚合函数 - count、sum、max、min、avg、collect_set/list - UDTF&#xff1a;…...