学习笔记十九——Rust多态
🧩 Rust 多态终极通俗指南
📚 目录导航
- 多态一句话概念
- 静态分派 vs 动态分派——根本差异
- 参数化多态(泛型)
3.1 函数里的泛型
3.2 结构体里的泛型
3.3 方法里的泛型
3.4 枚举里的泛型 - Ad hoc 多态(特例多态)
- 子类型多态(
dyn Trait动态分派) - 何时选哪种?极简速查 + 完整运行示例
- 高级技巧 & 常见坑
- 一句口诀快速回忆
1️⃣ 多态一句话概念
多态(Polymorphism)= “同一个调用,让不同类型各做各的事”——调用方不用关心实现细节。
现实类比:对不同门说“开门”,车门、房门、电梯门会各自打开,但指令只用写一次。
2️⃣ 静态分派 vs 动态分派——根本差异
| 维度 | 静态分派 | 动态分派 |
|---|---|---|
| 关键字 | <T> 泛型、普通 trait 约束 | dyn Trait(&dyn / Box<dyn> …) |
| 决定时机 | 编译期 | 运行期 |
| 编译器动作 | 单态化:为每种具体类型复制/生成专属机器码 | 生成 vtable(虚函数表,保存方法指针) |
| 运行成本 | 零 | 每次方法调用多一次 vtable 查表 + 间接跳转 |
| 代表场景 | 性能敏感、类型已知 | 插件系统、异构集合、运行期才能确定类型 |
3️⃣ 参数化多态(泛型)
用占位符
<T>写一次代码 → 编译期复制多份,零 运行开销。
3.1 函数里的泛型
// <T: Copy + Mul<Output=T>> —— T 要能 Copy 且能相乘
fn square<T: Copy + std::ops::Mul<Output = T>>(x: T) -> T {x * x
}fn main() {println!("{}", square(3)); // i32println!("{}", square(2.5)); // f64
}
运行输出
9
6.25
3.2 结构体里的泛型
#[derive(Debug)]
struct Point<T> { x: T, y: T }fn main() {let int_pt = Point { x: 1, y: 2 }; // Point<i32>let f_pt = Point { x: 1.0, y: 2.0 }; // Point<f64>println!("{:?}\n{:?}", int_pt, f_pt);
}
输出
Point { x: 1, y: 2 }
Point { x: 1.0, y: 2.0 }
3.3 方法里的泛型
#[derive(Debug)]
struct Point<T> { x: T, y: T }impl<T> Point<T> {// 方法再引入新的 Ufn mixup<U>(self, other: Point<U>) -> Point<U> {Point { x: other.x, y: other.y }}
}fn main() {let p1 = Point { x: 5, y: 10 };let p2 = Point { x: "左", y: "右" };let p3 = p1.mixup(p2);println!("{:?}", p3);
}
输出
Point { x: "左", y: "右" }
3.4 枚举里的泛型
#[derive(Debug)]
enum MyResult<T, E> {Ok(T),Err(E),
}fn main() {let ok : MyResult<u32, &str> = MyResult::Ok(200);let err : MyResult<u32, &str> = MyResult::Err("网络错误");println!("{:?}\n{:?}", ok, err);
}
输出
Ok(200)
Err("网络错误")
4️⃣ Ad hoc 多态(特例多态)
Ad hoc 来自拉丁语 ad hoc,意为“为此而生”“临时特制”。
在代码中就是:不同类型为同一接口各写一套实现。
use std::fmt::Display;trait Say { fn say(&self); } // 公共接口impl Say for String { // 为 String 定制fn say(&self) { println!("📢 字符串:{}", self); }
}impl Say for i32 { // 为 i32 定制fn say(&self) { println!("🔢 数字:{}", self); }
}fn shout<T: Say + Display>(x: T) { // 静态分派,无 vtablex.say();println!("(Display 再打印一次:{})", x);
}fn main() {shout("Hello".to_string());shout(42);
}
输出
📢 字符串:Hello
(Display 再打印一次:Hello)
🔢 数字:42
(Display 再打印一次:42)
5️⃣ 子类型多态(dyn Trait 动态分派)
5.1 关键词拆解
| 名词 | 通俗解释 |
|---|---|
dyn | dynamic,告诉编译器“运行期再决定真实类型” |
| Trait 对象 | 胖指针 (数据指针, vtable 指针) |
| vtable | 虚函数表:方法名 → 函数地址 |
5.2 内存示意图
Box<dyn Shape> (16 字节)
┌────────────┬────────────┐
│ data_ptr │ vtable_ptr │
└────────────┴────────────┘
data_ptr → 具体对象 (Circle / Square)
vtable_ptr→ [draw: fn, area: fn, ...]
5.3 最小示例
trait Draw { fn draw(&self); } // 统一接口struct Button { label: String }
struct Label { text: String }impl Draw for Button {fn draw(&self) { println!("🔘 按钮:{}", self.label); }
}
impl Draw for Label {fn draw(&self) { println!("🏷️ 标签:{}", self.text); }
}fn render(ui: &[Box<dyn Draw>]) { // 接受异构集合for w in ui { w.draw(); } // 运行期查 vtable
}fn main() {let ui: Vec<Box<dyn Draw>> = vec![Box::new(Button { label: "确定".into() }),Box::new(Label { text: "版本 1.0".into() }),];render(&ui);
}
输出
🔘 按钮:确定
🏷️ 标签:版本 1.0
6️⃣ 何时选哪种?极简速查 + 完整运行示例
| 场景 | 首选方案 | 为什么 |
|---|---|---|
| 性能极限、类型已知 | 泛型(参数化多态) | 单态化 ➜ 零成本 |
| 给现有类型统一接口 | Ad hoc 多态 | 各类型各写实现,仍静态分派 |
| 一个集合装不同类型 | Box<dyn Trait> | 运行期决定类型 |
| 隐藏返回值细节且想静态分派 | -> impl Trait | API 只露能力,内部零开销 |
6.1 Box<dyn Trait> & impl Trait 对比示例
trait Animal { fn sound(&self) -> &'static str; }// --------------------------- 动态分派 ---------------------------
struct Dog; struct Cat;
impl Animal for Dog { fn sound(&self) -> &'static str { "汪" } }
impl Animal for Cat { fn sound(&self) -> &'static str { "喵" } }fn zoo() { // 一个笼子装不同动物let list: Vec<Box<dyn Animal>> = vec![Box::new(Dog), Box::new(Cat)];for a in &list { println!("{}", a.sound()); } // vtable 调度
}// --------------------------- 隐藏返回类型 -------------------------
fn odds() -> impl Iterator<Item = i32> {(0..6).filter(|x| x % 2 == 1) // 返回类型被隐藏
}fn main() {zoo(); // 动态分派示例for n in odds() { print!("{} ", n); } // impl Trait 示例
}
输出
汪
喵
1 3 5
7️⃣ 高级技巧 & 常见坑
| 技巧 / 坑 | 说明 |
|---|---|
| Blanket Impl | impl<T: Display> ToString for T 一行给所有可 Display 的类型自动实现 to_string() |
| 对象安全规则 | 只有“对象安全”的 Trait 才能用 dyn Trait:① 不能有泛型方法;② 方法签名里不能直接用 Self 作为参数或返回值(除放在关联类型里) |
| 性能误区 | 动态分派的间接跳转成本很小,除非在紧密内层循环,否则无需过早优化 |
8️⃣ 一句口诀快速回忆
“能静不动,要扩用 Ad‑hoc;类型未知,用 dyn 出锅。”
- 参数化多态
<T>→ 静态分派,零成本 - Ad hoc 多态
trait + impl→ 静态分派,专属实现 - 子类型多态
dyn Trait→ 动态分派,vtable 调度 - 隐藏返回类型
-> impl Trait→ 静态分派 + 封装
相关文章:
学习笔记十九——Rust多态
🧩 Rust 多态终极通俗指南 📚 目录导航 多态一句话概念静态分派 vs 动态分派——根本差异参数化多态(泛型) 3.1 函数里的泛型 3.2 结构体里的泛型 3.3 方法里的泛型 3.4 枚举里的泛型Ad hoc 多态(特例多态࿰…...
交换机与路由器的主要区别:深入分析其工作原理与应用场景
在现代网络架构中,交换机和路由器是两种至关重要的设备。它们在网络中扮演着不同的角色,但很多人对它们的工作原理和功能特性并不十分清楚。本文将深入分析交换机与路由器的主要区别,并探讨它们的工作原理和应用场景。 一、基本定义 1. 交换…...
【Oracle专栏】Oracle中的虚拟列
Oracle相关文档,希望互相学习,共同进步 风123456789~-CSDN博客 1.背景 在EXP方式导出时,发现 出现如下提示 EXP-00107: virtual column 不支持,因此采用expdp方式导出。于是本文针对oracle虚拟列进行简单介绍。 2. 相…...
2020 年 7 月大学英语四级考试真题(组合卷)——解析版
🏠个人主页:fo安方的博客✨ 💂个人简历:大家好,我是fo安方,目前中南大学MBA在读,也考取过HCIE Cloud Computing、CCIE Security、PMP、CISP、RHCE、CCNP RS、PEST 3等证书。🐳 &…...
大语言模型的训练、微调及压缩技术
The rock can talk — not interesting. The rock can read — that’s interesting. (石头能说话,不稀奇。稀奇的是石头能读懂。) ----硅谷知名创业孵化器 YC 的总裁 Gar Tan 目录 1. 什么是大语言模型? 2. 语言建模ÿ…...
NEAT 算法解决 Lunar Lander 问题:从理论到实践
NEAT 算法解决 Lunar Lander 问题:从理论到实践 0. 前言1. 定义环境2. 配置 NEAT3. 解决 Lunar lander 问题小结系列链接0. 前言 在使用 NEAT 解决强化学习问题一节所用的方法只适用于较简单的强化学习 (reinforcement learning, RL) 环境。在更复杂的环境中使用同样的进化解…...
firewall指令
大家好,今天我们继续来了解服务管理,来看看打开或关闭指定端口,那么话不多说,开始吧. 1.打开或者关闭指定端口 在真正的生产环境,往往需要防火墙,但问题来了,如果我们把防火墙打开,那么外部请求数据包就不能跟服务器监听通讯,这时,需要打开指定的端口,比如80,22,8080等. 2.fi…...
【MySQL】MySQL表的增删改查(CRUD) —— 上篇
目录 MySQL表的增删改查(CRUD) 1. 新增(Create)/插入数据 1.1 单行数据 全列插入 insert into 表名 values(值, 值......); 1.2 单行数据 指定列插入 1.3 多行数据 指定列插入 1.4 关于时间日期(datetime&am…...
STM32的三种启动方式
目录 一、从主闪存存储器启动(Main Flash Memory) 二、从系统存储器启动(System Memory) 三、从内置SRAM启动(Embedded SRAM) 一、从主闪存存储器启动(Main Flash Memory) >&g…...
软考高级系统架构设计师-第15章 知识产权与标准化
【本章学习建议】 根据考试大纲,本章主要考查系统架构设计师单选题,预计考3分左右,较为简单。 15.1 标准化基础知识 1. 标准的分类 分类 内容 国际标准(IS) 国际标准化组织(ISO)、国际电工…...
Spring Boot 整合 DeepSeek 实现AI对话 (保姆及教程)
文章目录 文章目录 前言 一、创建 spring boot 工程 二、申请key 三、修改配置文件 application.properties 四、编写控制器(controller) 五、运行调试 前言 提示:随着人工智能的不断发展,ai这门技术也越来越重要,很多…...
Java File 类详解
Java File 类详解 File 类是 Java 中用于表示文件和目录路径名的抽象类,位于 java.io 包中。它提供了丰富的 API,用于操作文件系统,包括创建、删除、重命名、查询文件属性等功能。 1. File 类核心知识点 (1)构造方法…...
通过特定协议拉起 electron 应用
在 Android 通过 sheme 协议可以拉起其他应用。 electron 应用也可以通过类似特定协议被拉起。 在同时有 web、客户端的应用里,可以通过这种方式在 web 拉起客户端。 支持拉起客户端 const PROTOCOL xxxif (process.defaultApp) {// 这里是开发环境,有…...
前端与传统接口的桥梁:JSONP解决方案
1.JSONP原理 1.1.动态脚本注入 说明:通过创建 <script> 标签绕过浏览器同源策略 1.2.回调约定 说明:服务端返回 函数名(JSON数据) 格式的JS代码 1.3.自动执行 说明:浏览器加载脚本后立即触发前端预定义的回调函数(现代开…...
Vue3中provide和inject数据修改规则
在 Vue3 中,通过 inject 接收到的数据是否可以直接修改,取决于 provide 提供的值的类型和响应式处理方式: 1. 若提供的是普通值(非响应式数据) javascript 复制 // 父组件 provide(staticValue, 123); 子组件修改行…...
Mac-VScode-C++环境配置
mac上自带了clang所以不是必须下载Homebrew 下面是配置文件(注释记得删一下) package.json {"name": "git-base","displayName": "%displayName%","description": "%description%",&quo…...
Linux 文件系统目录结构详解
Linux 文件系统目录结构详解 Linux 文件系统遵循 Filesystem Hierarchy Standard (FHS) 标准,定义了各个目录的用途和文件存放规则。无论是开发者、运维工程师还是普通用户,理解这些目录的作用都至关重要。本文将全面解析 Linux 的目录结构,…...
编码器---正交编码器
一、正交编码器定义与核心作用 正交编码器(Orthogonal Encoder),又称增量式编码器,是一种通过输出两路相位差90的脉冲信号(A相、B相)来测量旋转角度、速度和方向的传感器。其核心优势是通过A/B相的脉冲顺序…...
Java Streams 使用教程
简介 Stream 是 Java 8 引入的一个 函数式编程特性,可以让我们用声明式的方式操作集合(如 List、Set、Map 等)。 核心作用是: 从集合中提取数据(流) 对数据做中间操作(filter/map/sort...&am…...
1001: 自由落体的计算
题目描述 一球从m米高度自由下落,每次落地后返回原高度的一半,再落下。 求它在第n次触地时会反弹多高?直到第n次触地时共经过多少米? 输入 一行,包含两个数m, n 其中0 < m < 1,000,000,000 0 < n < 1,000,000,000 输…...
开发环境解决浏览器层面跨域问题
适用于开发环境临时调试等情况 新建一个 Chrome 的快捷方式,目标后面跟上: –disable-web-security --disable-gpu --user-data-dir%LOCALAPPDATA%\Google\chromeTemp 打开后会给出不安全的提示...
2025年渗透测试面试题总结-拷打题库07(题目+回答)
网络安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 2025年渗透测试面试题总结-拷打题库07 1. CMS目录扫描的意义 2. 常见网站服务器容器 3. MySQL写入We…...
【leetcode刷题日记】lc.300-最长递增子序列
目录 1.题目 2.代码 1.题目 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,…...
游戏引擎学习第236天:GPU 概念概述
回顾并展望通过视频采集卡进行流媒体传输的未来 昨天,我们迈出了大胆的一步,决定初始化硬件的 3D 加速,因为我有点厌倦了我们的游戏没有垂直同步(vsync)。如今,在 Windows 上,我找不到一种可靠…...
深入理解Linux中的线程控制:多线程编程的实战技巧
个人主页:chian-ocean 文章专栏-Linux 前言: POSIX线程(Pthreads) 是一种在 POSIX 标准下定义的线程库,它为多线程编程提供了统一的接口,主要用于 UNIX 和类 UNIX 系统(如 Linux、MacOS 和 BS…...
【题解-Acwing】790. 数的三次方根
题目:790. 数的三次方根 题目描述 给定一个浮点数 n,求它的三次方根。 输入 共一行,包含一个浮点数 n 。 输出 共一行,包含一个浮点数,表示问题的解。 注意,结果保留 6 位小数。 数据范围 −10000 ≤ n ≤ 10000 时空限制 1s / 64MB 输入样例 1000.00输出样…...
【条形码识别改名工具】如何批量识别图片条形码,并以条码内容批量重命名,基于WPF和Zxing的开发总结
批量图片条形码识别与重命名系统 (WPF + ZXing)开发总结 项目适用场景 电商商品管理:批量处理商品图片,根据条形码自动分类归档图书馆系统:扫描图书条形码快速建立电子档案医疗档案管理:通过药品条形码整理医疗图片资料仓储管理:自动化识…...
大模型微服务架构模块实现方案,基于LLaMA Factory和Nebius Cloud实现模型精调的标准流程及代码
以下是基于LLaMA Factory和Nebius Cloud实现模型精调的标准流程及代码示例,结合最新技术动态和行业实践整理: 一、LLaMA Factory本地部署方案 1. 环境配置 # 创建Python环境并安装依赖 conda create -n llama_factory python3.10 conda activate llam…...
【C++】 —— 笔试刷题day_22
一、添加字符 题目解析 这道题,给定两个字符串A和B,字符串A的长度要小于B的长度; 现在我们要对A字符串添加字符,使得A字符串长度等于B字符串的长度,并且要求对应位置的字母尽量相等,然后求出来不相等的字符…...
深入浅出:LDAP 协议全面解析
在网络安全和系统管理的世界中,LDAP(轻量级目录访问协议,Lightweight Directory Access Protocol)是一个不可忽视的核心技术。它广泛应用于身份管理、认证授权以及目录服务,尤其在企业级环境中占据重要地位。本文将从基…...
