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、相似点
-
定义行为:
- Rust Traits:用于定义一组方法签名,这些方法可以在实现该特征的类型上调用。
- Java Interfaces:用于定义一组方法签名,这些方法可以在实现该接口的类上调用。
-
多重实现:
- Rust Traits:一个类型可以实现多个特征。
- Java Interfaces:一个类可以实现多个接口。
-
抽象方法:
- Rust Traits:可以包含没有默认实现的方法,这些方法必须由实现特征的类型来实现。
- Java Interfaces:可以包含抽象方法,这些方法必须由实现接口的类来实现。
2、不同点
-
默认实现:
- Rust Traits:允许为方法提供默认实现。如果类型不提供自己的实现,则使用默认实现。
- Java Interfaces:从 Java 8 开始,接口可以包含默认方法(default methods),但这在历史上并不是一直存在的。
-
关联类型与泛型:
- Rust Traits:支持关联类型(associated types)和泛型参数,使得特征更加灵活。例如,可以定义一个特征
Iterator,它有一个关联类型Item。 - Java Interfaces:主要通过泛型来实现类似的功能,但没有直接的关联类型概念。
- Rust Traits:支持关联类型(associated types)和泛型参数,使得特征更加灵活。例如,可以定义一个特征
-
静态分发与动态分发:
- Rust Traits:默认情况下,特征方法调用是静态分发的(即在编译时确定)。可以通过特征对象(trait objects)实现动态分发(即运行时确定)。
- Java Interfaces:接口方法调用通常是动态分发的(即通过虚方法表在运行时确定)。
-
所有权与生命周期:
- 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 特征编程
昨夜江边春水生,艨艟巨舰一毛轻。 向来枉费推移力,此日中流自在行。 ——《活水亭观书有感二首其二》宋朱熹 【哲理】往日舟大水浅,众人使劲推船,也是白费力气,而此时春水猛涨,巨舰却自由自在地飘行在水流中…...
彻底理解哈希表(HashTable)结构
目录 介绍优缺点概念哈希函数快速的计算键类型键转索引霍纳法则 均匀的分布 哈希冲突链地址法开放地址法线性探测二次探测再哈希法 扩容/缩容实现哈希创建哈希表质数判断哈希函数插入&修改获取数据删除数据扩容/缩容函数全部代码 哈希表(Hash Table)…...
微信小程序的汽车维修预约管理系统
文章目录 项目介绍具体实现截图技术介绍mvc设计模式小程序框架以及目录结构介绍错误处理和异常处理java类核心代码部分展示详细视频演示源码获取 项目介绍 系统功能简述 前台用于实现用户在页面上的各种操作,同时在个人中心显示各种操作所产生的记录:后…...
LeetCode:3255. 长度为 K 的子数组的能量值 II(模拟 Java)
目录 3255. 长度为 K 的子数组的能量值 II 题目描述: 实现代码与解析: 模拟 原理思路: 3255. 长度为 K 的子数组的能量值 II 题目描述: 给你一个长度为 n 的整数数组 nums 和一个正整数 k 。 一个数组的 能量值 定义为&am…...
深入了解逻辑回归:机器学习中的经典算法
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
软件测试基础十三(python 函数)
函数 1. 函数的意义 代码复用 提高效率:Python中的函数允许将一段可重复使用的代码封装起来。例如,在一个数据分析项目中,可能需要多次计算一组数据的平均值。可以将计算平均值的代码定义为一个函数: def calculate_average(nu…...
计算机网络——HTTP篇
基础篇 IOS七层网络模型 TCP/IP四层模型? 应⽤层:位于传输层之上,主要提供两个终端设备上的应⽤程序之间的通信,它定义了信息交换的格式,消息会交给下⼀层传输层来传输。 传输层的主要任务就是负责向两台设备进程之间…...
信息化运维方案,实施方案,开发方案,信息中心安全运维资料(软件资料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 摘要 概念:gulp用于自动化开发流程。 理解:我们只需要编写任务,然后gulp帮我们执行 核心概念: 任务:通过定义不同的任务来组织你的构建流程。 管道:通过管道方式将文件从一个插件传递…...
css实现div被图片撑开
固定好盒子的宽度,高度随传过来的图片大小决定 <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(Data Analysis Expressions)是一种用于 Power Pivot、Power BI 和 SQL Server Analysis Services 的公式语言,旨在帮助用户进行数据建模和复杂计算。DAX 的设计初衷是使数据分析变得简单而高效,特别是在处理数据模型中的表关系…...
大模型应用编排工具Dify二开之工具和模型页面改造
1.前言 简要介绍下 dify: 一款可以对接市面上主流大模型的任务编排工具,可以通过拖拽形式进行编排形成解决某些业务场景的大模型应用。 背景信息: 环境:dify-0.8.3、docker-21 最近笔者在做 dify的私有化部署和二次…...
Pytorch用BERT对CoLA、新闻组文本数据集自然语言处理NLP:主题分类建模微调可视化分析...
原文链接:https://tecdat.cn/?p38181 自然语言处理(NLP)领域在近年来发展迅猛,尤其是预训练模型的出现带来了重大变革。其中,BERT 模型凭借其卓越性能备受瞩目。然而,对于许多研究者而言,如何高…...
LightGBM-GPU不能装在WSL,能装在windows上
这是一篇经验总结文章,注重思路,忽略细节。 1.起因 用多个机器学习方法训练模型,比较性能,发现Light GBM方法获得的性能明显更高,但问题是在CPU上训练的速度特别特别慢,需要用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,请按照此处的说明进行操作:Foundry 安装 Foundry Hello World 只需运行以下命令,它将为你设置环境,创建测试并运行它们。(当然,这假设你已经安装了 Foundry&…...
idea database连接数据库后看不到表解决方法、格式化sql快捷键
最下面那个勾选上就可以了 或 格式化sql快捷键: 先选中, 使用快捷键格式化 SQL: Windows/Linux: Ctrl Alt L macOS: Cmd Alt L...
【数学二】线性代数-向量-向量组的秩、矩阵得秩
考试要求 1、理解 n n n维向量、向量的线性组合与线性表示的概念. 2、理解向量组线性相关、线性无关的概念,掌握向量组线性相关、线性无关的有关性质及判别法. 3、了解向量组的极大线性无关组和向量组的秩的概念,会求向量组的极大线性无关组及秩. 4、了解向量组等价的概念,…...
ABAP开发-内存管理
系列文章目录 文章目录 系列文章目录前言一、概述二、程序间调用三、外部会话和内部会话四、SAP内存与ABAP内存五、实例总结 前言 一、概述 内存是程序之间为了传递数据而使用的共享存储空间,在每个程序里使用的内存有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 公司提出,是浏览器的一种安全策…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
