Rust By Practice 实战:掌握动态数组 Vec 的增删、转换、切片与容量管理
文档教程示例工程【免费下载链接】rust-by-practiceRust By Practice will evolve into Origin.项目地址https://gitcode.com/gh_mirrors/ru/rust-by-practice点击查看免费下载VecT是 Rust 标准库中最常用的堆上动态数组与定长数组[T; N]相比它最大的特点是长度可以在运行时动态调整。本篇基于 Rust By Practice 集合类型章节的 Vector 练习单元通过 8 道分级练习题系统讲解Vec的创建与增删元素、From/Into类型转换、索引访问、切片借用、容量capacity与内存分配策略以及如何在同一个Vec中存储不同类型的元素。学完本篇你将能够写出高性能、零多余内存分配且类型安全的动态数组代码。认识 Vector可动态调整长度的数组Rust 中用于承载同类型元素的连续序列有两种核心形式定长数组[T; N]长度在编译期固定无法在运行时增删元素动态数组VecT长度在运行时可变元素连续存储在堆上可以随时push或pop。Vec与切片、字符串之间存在紧密的对应关系[T]之于VecT正如str之于String。String的底层正是Vecu8。若想系统回顾定长数组与切片可参考仓库中的 数组章节 与 切片章节而Vec在集合类型章节中的位置可见 SUMMARY.md 中集合类型一节。创建 Vec 的三种基本方式练习 1 展示了Vec的三种常用创建方式fn main() { let arr: [u8; 3] [1, 2, 3]; // 方式一由数组通过 From 转换而来 let v Vec::from(arr); is_vec(v); // 方式二使用 vec! 宏方括号形式 let v vec![1, 2, 3]; is_vec(v); // 方式三vec! 宏的花括号形式与方括号完全等价 let v vec!(1, 2, 3); is_vec(v); println!(Success!) } fn is_vec(v: Vecu8) {}需要特别注意的是vec!(arr)中的arr是[u8; 3]类型的数组因此v1的类型是Vec[u8; 3]而不是Vecu8。官方题解见 Vector.md 题解要求用Vec::new配合for循环重写v1的构造将数组中的元素逐个取出压入新的Vec// 题解用 Vec::new for 循环逐元素构造 Vecu8 let mut v1 Vec::new(); for i in v { v1.push(*i); }这道题同时揭示了vec!宏的语法细节宏可以使用[]、()、{}三种定界符它们只是书写形式不同语义完全一致。用 pop 与 push 增删元素pop从尾部移除并返回最后一个元素push从尾部追加一个元素。练习 2 先用这两者将v1调整为[1, 2, 3]再要求你用extend方法批量扩展v2// 填空 fn main() { let mut v1 Vec::from([1, 2, 4]); v1.pop(); // 移除 4v1 变为 [1, 2] v1.push(3); // 追加 3v1 变为 [1, 2, 3] let mut v2 Vec::new(); v2.extend([1, 2, 3]); // 批量追加可迭代序列 assert_eq!(v1, v2); println!(Success!) }extend接收任何实现了IntoIterator的类型数组、切片、迭代器等可以一次性把多个元素追加到Vec尾部比逐个push更高效、更简洁。将其他类型转换为 VecFrom / Into 特征Vec的另一个强大之处在于标准库为它实现了多个FromT特征。只要某类型T为Vec实现了FromT那么T就能被转换成Vec。关于From/Into特征的通用机制可参考仓库中的 From/Into 章节实现了impl FromT for U之后U::from(T)与T.into()两种写法均可使用后者由Into自动派生。练习 3 集中考察了四类常见转换// 填空 fn main() { // array - Vecimpl From[T; N] for Vec let arr [1, 2, 3]; let v1 Vec::from(arr); let v2: Veci32 arr.into(); assert_eq!(v1, v2); // String - Vecimpl FromString for Vec按字节取出 let s hello.to_string(); let v1: Vecu8 s.into(); let s hello.to_string(); let v2 s.into_bytes(); assert_eq!(v1, v2); // str - Vecimpl_ From_ str for Vec let s hello; let v3 Vec::from(s); assert_eq!(v2, v3); // 迭代器 - Vec通过 collect 收集 let v4: Veci32 [0; 10].into_iter().collect(); assert_eq!(v4, vec![0; 10]); println!(Success!) }四种转换的关键点源类型转换方式结果类型说明[T; N]数组Vec::from(arr)或arr.into()VecT基于impl From[T; N] for VecStrings.into()或s.into_bytes()Vecu8字符串按字节序列取出strVec::from(s)Vecu8借用字符串的字节迭代器iterator.collect()VecT配合类型标注使用注意使用into()时通常需要显式标注目标类型如let v2: Veci32 arr.into()因为编译器无法自动推断转换目标。而String::into_bytes()返回Vecu8这是字符串转字节最直白的途径。索引访问与越界问题Vec支持通过下标v[i]直接访问元素但下标越界会直接触发 panic。练习 4 的第一段循环for i in 0..5访问只有 3 个元素的v[i]正是为了让你亲身体验越界fn main() { let mut v Vec::from([1, 2, 3]); for i in 0..5 { println!({:?}, v[i]) // i 达到 3、4 时越界 panic } // ... }官方题解给出了两种修复思路思路一改用get(i)返回OptionT并配合if let安全兜底let mut v Vec::from([1, 2, 3]); for i in 0..5 { println!({:?}, v.get(i)) // 越界时返回 None不 panic } for i in 0..5 { if let Some(x) v.get(i) { v[i] x 1; // 已有元素则 1 } else { v.push(i 2); // 越界位置则扩容补元素 } } assert_eq!(v, vec![2, 3, 4, 5, 6]);思路二保证循环范围不超过元素数量let mut v Vec::from([1, 2, 3, 4, 5]); for i in 0..5 { v[i] 1; // 5 个元素恰好对应 0..5 } assert_eq!(v, vec![2, 3, 4, 5, 6]);这组对比说明了索引访问的取舍直接下标访问v[i]性能最佳但越界即 panicget(i)返回OptionT更安全适合不确定下标是否合法、需要优雅兜底的场景。切片Slicing只读借用的最佳实践与String的切片类似Vec也可以通过获取切片。Vec是可变的而它的切片是不可变只读借用当然也可以获取可变切片mut [T]。在 Rust 中当函数只需要读取数据时优先传递切片[T]而不是VecT切片的借用不会发生所有权转移no move也不会产生拷贝no copy更加灵活高效。这正对应str与String的关系——[T]之于VecT如同str之于String。练习 5 集中考察了切片的使用与误区// 修复错误 fn main() { let mut v vec![1, 2, 3]; let slice1 v[..]; // 全切片等价于 v[0..v.len()] // 越界访问将导致 panic修改时必须使用 v.len let slice2 v[0..3]; // 原题的 v[0..4] 越界需改为 v.len 范围内的值 assert_eq!(slice1, slice2); // 注意切片和 Vec 是不同的类型 // Vec 仅仅是 Vec 的引用可通过解引用获取 Vec 并调用其方法 let vec_ref: mut Veci32 mut v; (*vec_ref).push(4); // 通过 mut Vec 修改底层数组 // 可变切片对切片的修改会同步影响底层 Vec let slice3 mut v[0..4]; slice3[3] 42; // 切片没有 push 方法只能按下标写入 assert_eq!(slice3, [1, 2, 3, 42]); assert_eq!(v, [1, 2, 3, 42]); println!(Success!) }该练习中的两个关键修正点对照 题解切片越界v[0..4]对只有 3 个元素的v越界必须用v.len()作为上限。切片没有push原题中的slice3.push(4)无法编译——切片[T]是定长视图不支持扩容只能通过下标写入既有位置。可变切片写入会直接反映到其底层的Vec上。同时注意mut Veci32与mut [i32]是两个不同的类型前者是对Vec结构本身的引用可以解引用后调用push等方法后者是对底层连续内存区的可变视图。容量Capacity与内存分配策略容量capacity是Vec已经分配好的内存空间用于存储未来追加的元素长度len是当前Vec中实际存储的元素数量。二者是理解Vec性能的核心概念。当新元素加入使len将要超过capacity时Vec会自动扩容Rust 会重新分配一块更大的内存并把旧元素整体拷贝过去。目前 Rust 的容量调整策略是加倍增长例如 2 → 4 → 8 → 16 …。如果扩容频繁发生反复的内存分配与拷贝会显著拖慢系统性能因此最好的做法是在创建时用Vec::with_capacity提前分配足够的容量尽量减少分配次数。练习 6 通过一个完整的观察实验验证了上述机制// 修复错误 fn main() { let mut vec Vec::with_capacity(10); // 提前分配了 10 的容量但还没有元素 assert_eq!(vec.len(), 0); assert_eq!(vec.capacity(), 10); // 由于提前设置了足够的容量前 10 次 push 不触发任何内存分配 for i in 0..10 { vec.push(i); } assert_eq!(vec.len(), 10); assert_eq!(vec.capacity(), 10); // 第 11 个元素超出容量触发重新分配 vec.push(11); assert_eq!(vec.len(), 11); assert!(vec.capacity() 11); // 填写合适的值让下面的 for 循环全程不发生内存分配 let mut vec Vec::with_capacity(100); for i in 0..100 { vec.push(i); } assert_eq!(vec.len(), 100); assert_eq!(vec.capacity(), 100); println!(Success!) }关键结论均可用 题解 中的断言验证len()与capacity()是两个独立的概念容量 10、长度 0 表示空数组但已预留 10 个元素的空间只要push数量不超过容量capacity保持不变、不发生内存分配当len即将超过capacity时触发重新分配capacity至少翻倍这也是最后一道填空必须填100的原因——恰好与循环的 100 次push匹配从而做到零分配。在同一个 Vec 中存储不同类型的元素Vec要求元素类型完全一致vec![1, 2.0, 3]这类混合i32与f64的写法会直接编译报错。要在一个Vec中容纳不同类型标准做法有两种枚举与特征对象。方案一使用枚举包装练习 7 用枚举把V4(String)与V6(String)两种变体统一为IpAddr类型#[derive(Debug, PartialEq)] enum IpAddr { V4(String), V6(String), } fn main() { // 填空 let v: VecIpAddr vec![ IpAddr::V4(127.0.0.1.to_string()), IpAddr::V6(::1.to_string()), ]; // 枚举的比较需要派生 PartialEq 特征 assert_eq!(v[0], IpAddr::V4(127.0.0.1.to_string())); assert_eq!(v[1], IpAddr::V6(::1.to_string())); println!(Success!) }注意原题的#[derive(Debug)]不足以支撑assert_eq!比较题解中补充了PartialEq派生——枚举之间进行比较必须实现PartialEq特征。方案二使用特征对象Box dyn Trait练习 8 改用特征对象为两个结构体实现同一个IpAddr特征再用Boxdyn IpAddr装箱后存入Vectrait IpAddr { fn display(self); } struct V4(String); impl IpAddr for V4 { fn display(self) { println!(ipv4: {:?}, self.0) } } struct V6(String); impl IpAddr for V6 { fn display(self) { println!(ipv6: {:?}, self.0) } } fn main() { // 填空VecBoxdyn IpAddr let v: VecBoxdyn IpAddr vec![ Box::new(V4(127.0.0.1.to_string())), Box::new(V6(::1.to_string())), ]; for ip in v { ip.display(); } }两种方案的取舍枚举变体集合封闭、编译期可知类型安全且无动态分发开销但每增加一种类型都要修改枚举定义特征对象Boxdyn Trait变体开放、运行时多态新增类型无需改动既有代码但会引入一次指针间接寻址与动态分发的开销。若想深入了解特征对象与Box的机制可进一步阅读仓库中的 特征对象章节 与 Box 章节。练习答案获取本篇所有练习的官方参考答案均存放在仓库的 solutions/collections/Vector.md 中完成练习后可对照题解核对自己的实现同时仓库提供了 link_solution 脚本可以快速建立练习与题解之间的关联便于逐个题目校验。建议先独立完成练习题再对照题解效果最佳。赞分享文档教程示例工程【免费下载链接】rust-by-practiceRust By Practice will evolve into Origin.项目地址https://gitcode.com/gh_mirrors/ru/rust-by-practice点击查看免费下载相关推荐Mac Mouse Fix终极指南让普通鼠标在macOS上超越触控板体验Mac Mouse Fix终极指南让普通鼠标在macOS上超越触控板体验 还在为macOS上鼠标体验不佳而烦恼吗Mac Mouse Fix是一款专为macO文档教程示例工程Rustlings 教程掌握 Rust Vector动态数组——栈与堆、vec! 宏与增删遍历实战Rustlings 教程掌握 Rust Vector动态数组——栈与堆、vec! 宏与增删遍历实战 向量 VecT 是 Rust 中使用频率最高的数教程CLI示例工程生产级Parachain参考实现Cumulus驱动的Asset Hub与Bridge Hub架构分析生产级Parachain参考实现Cumulus驱动的Asset Hub与Bridge Hub架构分析 Cumulus 是在 Substrate 之上编写 P文档教程示例工程上一篇终极CoreCycler教程从零开始掌握CPU稳定性测试与优化下一篇gogcli 删除 Gmail 发送别名send-as命令 gog gmail settings sendas delete 完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Apache Arrow C++ 行列转换实战:行式数据与列式 Table 的双向转换

Apache Arrow C++ 行列转换实战:行式数据与列式 Table 的双向转换

Apache Arrow C 行列转换实战:行式数据与列式 Table 的双向转换 【免费下载链接】arrow Apache Arrow is a multi-language toolbox for accelerated data interchange and in-memory processing 项目地址: https://gitcode.com/gh_mirrors/arrow12/arrow Ap…

2026/9/22 19:07:14 阅读更多 →
adata源码拆解:3个核心逻辑搞定高频面试题

adata源码拆解:3个核心逻辑搞定高频面试题

adata源码拆解:3个核心逻辑搞定高频面试题 官方文档翻了三遍还是云里雾里?别急,直接看源码。 很多开发者卡在 adata 这类底层数据组件上,不是代码写不出来,而是 抓不住重点…

2026/9/22 19:07:14 阅读更多 →
3个维度拆解教育教学管理论文,面试必问避坑指南

3个维度拆解教育教学管理论文,面试必问避坑指南

3个维度拆解教育教学管理论文,面试必问避坑指南 刚接手教育教学管理论文的项目,或者准备相关技术岗位面试,是不是经常遇到这种情况?从网上复制一段关于论文查重、格式处理或者数据可视化的代码,丢进本地环境,结果直接报错…

2026/9/22 19:07:14 阅读更多 →

最新新闻

正能量的句子经典从入门到实战

正能量的句子经典从入门到实战

5个技巧搞定正能量句子经典,告别文档焦虑 官方文档动辄几百页,翻了三遍还是不知道哪句能用?别慌,这不仅是你的问题,更是大多数内容创作者的痛点。很多教程只给定义,不给场景,导致你收藏了一堆“正能量的句子经典”,却在写文案时脑子一片空白。今天不…

2026/9/22 19:38:38 阅读更多 →
如何做好招商工作速查手册

如何做好招商工作速查手册

做好招商工作5个关键点:从原理到性能优化实战 面试被问原理答不上来?别慌,这不仅是理论盲区,更是实战脱节。很多开发者在性能优化面前卡壳,根源在于没把“招商”这类业务逻辑和底层执行效率打通。招商不是喊口号,而是像代码一样,要有明确的入口、清晰…

2026/9/22 19:38:38 阅读更多 →
3年老兵教你一文搞懂dnf影舞者用什么武器避坑指南

3年老兵教你一文搞懂dnf影舞者用什么武器避坑指南

3年老兵教你一文搞懂dnf影舞者用什么武器避坑指南 别划走。如果你也是那种看了一堆教程,代码复制粘贴能跑,但换个场景就懵,甚至不知道从哪下手写项目的老哥,这篇就是救你的。我们不再讲那些虚头巴脑的大道理,直接上干货。…

2026/9/22 19:38:38 阅读更多 →
3步解决你没有好结果:源码解析避坑指南

3步解决你没有好结果:源码解析避坑指南

3步解决你没有好结果:源码解析避坑指南 配置环境就卡半天,是不是你也遇到过?明明照着文档敲代码,控制台却报出一堆看不懂的红字,或者运行后 你没有好结果…

2026/9/22 19:38:38 阅读更多 →
小牛官网首页改版踩坑记:5个最佳实践让性能提升3倍

小牛官网首页改版踩坑记:5个最佳实践让性能提升3倍

小牛官网首页改版踩坑记:5个最佳实践让性能提升3倍 刚接到一个需求,要把内部的小牛官网首页重构一下。看着挺简单,不就是换个模板、加几个新组件嘛?结果一跑起来,页面加载时间从原来的800毫秒飙到了3.5秒,首屏白屏时间更是让人抓狂。更糟糕的是…

2026/9/22 19:38:37 阅读更多 →
别被官方文档绕晕了,一文搞懂女王谷地图核心逻辑

别被官方文档绕晕了,一文搞懂女王谷地图核心逻辑

别被官方文档绕晕了,一文搞懂女王谷地图核心逻辑 还在对着几十页的 PDF 文档抓头发吗?那种“读了开头忘了结尾,看完例子还是不会写”的绝望感,相信做开发的都懂。今天咱们不整那些虚头巴脑的理论,直接把【女王谷地图】的底层逻辑拆碎了喂给你。…

2026/9/22 19:37:36 阅读更多 →

日新闻

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天 配置环境就卡半天?别怪机器慢,多半是你没选对工具链。在Java、Go或Python的项目现场, 手写实现…

2026/9/22 0:00:41 阅读更多 →
剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑 面试被问原理答不上来,是不是常态?别慌。很多开发者对着 GitHub 开源仓库里的代码发呆,看似简单实则暗藏玄机。今天这份【剑帝加点】速查手册,直接带你拆解核心实现,把面试必考的原理讲透。…

2026/9/22 0:00:41 阅读更多 →
手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优 复制来的代码跑不通不知道怎么调?别慌,这种“复制粘贴地狱”在开发圈太常见了。尤其是做 图片压缩网站…

2026/9/22 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/22 4:32:41 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/22 4:38:57 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/22 8:51:04 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/21 15:36:51 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/21 15:36:51 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/22 2:43:42 阅读更多 →