文档教程【免费下载链接】patternsA catalogue of Rust design patterns, anti-patterns and idioms项目地址https://gitcode.com/gh_mirrors/pa/patterns点击查看免费下载本指南整理自当前仓库 Rust Design Patterns 目录 中的 Structural Pattern「Struct Decomposition for independent borrowing」结合仓库内相关反模式与结构性模式文档进行展开。当你的大结构体在借用检查器面前牵一发动全身——明明只想借一个字段却被要求交出整个结构体时把大结构体拆成若干小结构体、再组合回去是绕开限制并顺手提升设计的实用方案。读完本文你将掌握该模式的触发场景、重构手法、利弊边界以及它与仓库中Clone反模式、Newtype 模式之间的辨析。问题背景借用检查器为何连坐整个结构体Rust 的借用规则只有两条硬约束要么存在一个可变引用要么同时存在多个不可变引用二者不可兼得。日常编码中这一约束大多数时候是友好的——因为借用检查器能够识别字段级借用field-level borrowinga.b和a.c属于不同内存区域可以分别被独立借用编译器不会因此把整个a一并冻结。问题出在函数边界。当你把一个Database整体传给某个函数时函数接收的是对整个结构体的引用而不是其中某个字段的引用。于是这个整结构体不可变借用会与你在调用前已经持有的字段可变借用发生冲突——即使该函数实际只用到了其中几个字段。这正是 compose-structs.md 里给出的第一个例子要演示的失败场景结构体整体被当作参数传递导致此前对单个字段的可变借用无法继续使用。触发场景一份会编译失败的示例代码原文档给出如下人为设计但真实典型的例子。一个数据库配置结构体有三个字段struct Database { connection_string: String, timeout: u32, pool_size: u32, } fn print_database(database: Database) { println!(Connection string: {}, database.connection_string); println!(Timeout: {}, database.timeout); println!(Pool size: {}, database.pool_size); } fn main() { let mut db Database { connection_string: initial string.to_string(), timeout: 30, pool_size: 100, }; let connection_string mut db.connection_string; print_database(db); *connection_string new string.to_string(); }这段代码的逻辑非常直白先对db.connection_string取一个可变借用打算稍后改写它期间调用print_database(db)打印整个数据库配置。编译器给出的错误信息如下let connection_string mut db.connection_string; ------------------------- mutable borrow occurs here print_database(db); ^^^ immutable borrow occurs here *connection_string new string.to_string(); ------------------ mutable borrow later used here错误链条一目了然第 1 行对db.connection_string产生一个可变借用第 2 行print_database(db)要求对整个db的不可变借用与现存的可变借用重叠冲突爆发第 3 行此前那个可变借用在此处还要继续使用于是编译器判定冲突无法调和。注意print_database内部明明只读取了三个字段、没有写操作但它接受的参数类型是Database这个签名本身就要求整个结构体不可变。问题不在函数实现而在函数签名把结构体当成一个不可分割的整体。模式定义拆小、组合、再独立借用原文档对该模式的描述可以浓缩为三步当一个大型结构体在借用检查器面前引发问题时——虽然字段本可被独立借用但结构体经常被整体使用作为整体传给函数、整体被持有等从而阻断了其他使用方式——解决方案是把大结构体分解为若干更小的结构体再把这些小结构体组合回原来的结构体。此后每个小结构体可以被单独借用行为更加灵活。分解的额外红利在于这个重构过程常常会暴露更小的功能单元让设计在其它维度上变得更好——这正是下文优点部分提到的往往产出更好的设计的机制所在。重构示例三个小结构体组合回Database对上面的失败案例原文档给出了完整的重构方案把Database的三个字段各自包装为一个小结构体采用 Newtype 风格再组合成原来的Database// Database 现在由三个结构体组成——ConnectionString、Timeout 和 PoolSize。 // 让我们把它分解为更小的结构体 #[derive(Debug, Clone)] struct ConnectionString(String); #[derive(Debug, Clone, Copy)] struct Timeout(u32); #[derive(Debug, Clone, Copy)] struct PoolSize(u32); // 然后把这三个小结构体组合回 Database struct Database { connection_string: ConnectionString, timeout: Timeout, pool_size: PoolSize, } // print_database 改为接收 ConnectionString、Timeout 和 PoolSize 三个小结构体 fn print_database(connection_str: ConnectionString, timeout: Timeout, pool_size: PoolSize) { println!(Connection string: {connection_str:?}); println!(Timeout: {timeout:?}); println!(Pool size: {pool_size:?}); } fn main() { // 用三个小结构体初始化 Database let mut db Database { connection_string: ConnectionString(localhost.to_string()), timeout: Timeout(30), pool_size: PoolSize(100), }; let connection_string mut db.connection_string; print_database(connection_string.clone(), db.timeout, db.pool_size); *connection_string ConnectionString(new string.to_string()); }重构后代码可以正常编译关键差异在于print_database的参数类型从Database整结构体不可变借用变成了按值接收的三个小结构体。Timeout与PoolSize实现了Clone Copy传值即拷贝ConnectionString实现了Clone调用处通过connection_string.clone()传入一份副本。这样一来db.connection_string的可变借用全程保持活跃print_database不再触碰db本身两者互不干扰。db.timeout、db.pool_size是Copy类型传值时自动复制连借用都不需要产生。原文档在此特意为小结构体实现了Clone/Copy。这一点值得展开分解模式与克隆经常搭配出现但二者职责不同——分解解决的是借用粒度问题克隆解决的是传值 vs 借用的取舍问题。如果你的小结构体语义上适合按值传递如配置、度量这类小而廉价的类型Copy可以让调用完全绕开借用冲突。什么时候该用这个模式Motivation原文档的动机段落非常克制当你拥有一个字段众多、且你希望这些字段被独立借用的大结构体时这个模式最有用最终获得更灵活的行为。从仓库上下文structural/intro.md 引用 Wikipedia 对结构型模式的定义通过识别实体之间关系的简单实现方式来简化设计来看这个模式属于 Rust 结构型设计模式家族与偏好小 cratesmall-crates.md、把 unsafe 关进小模块unsafe-mods.md共享同一种设计哲学通过缩小单元的粒度来获得组合上的自由。优点原文档明确列出两点绕开借用检查器的限制结构体分解让你能分别借用各部分而不必每次都交出整个结构体从而突破整体使用造成的借用冲突。常常带来更好的设计拆分过程本身就是一次领域建模往往会浮现出原本被大结构体掩盖的独立功能单元促使接口更清晰。缺点与代码异味原文档同样坦诚地列出代价代码更啰嗦每个小结构体需要单独定义常常还要派生Clone、Copy、Debug等 trait调用点也更繁琐。小结构体可能不是好的抽象强行拆出来的单元如果语义上站不住脚反而得到更差的设计。原文档特别指出这大概率是一个代码异味code smell暗示程序应当在其它层面进行重构——例如也许这个大结构体本就不该存在也许应当拆成真正独立的类型而不是为了绕过借用检查器而硬拆。这条异味提示与仓库中的反模式文档形成了绝佳对照。仓库 borrow_clone.md 记录的反模式是用.clone()来满足借用检查器——开发者为了消除借用错误而随手克隆结果是数据被复制、两处修改不同步还白白付出性能代价。二者放在一起看Clone反模式不动结构靠复制数据逃避借用检查会引入真正的运行时开销结构体分解模式动结构靠缩小借用粒度解决问题开销近乎为零还顺带整理设计。当你想拆结构体时先问自己是否其实只想克隆一个廉价字段如果是也许该用反模式文档中提醒的方式运行cargo clippy检测不必要的 clone、认真阅读所有权相关章节重新审视需求。反过来如果确实存在大量需要并行可变访问的字段分解是比克隆更干净的出路。讨论为什么这是 Rust 特有的模式原文档的讨论部分点明了该模式的本质定位在没有借用检查器的语言里这个模式根本不需要——从这个意义上说它是Rust 独有的模式。不过把功能拆成更小的单元常常带来更干净的代码是独立于语言的、广泛认可的软件工程原则。换句话说该模式有两层价值在 Rust 语境下它是对抗借用检查器限制的战术手段在通用软件工程语境下它是单一职责模块化思想的又一次落地。模式的有效性完全建立在 Rust 借用检查器的字段级独立借用能力之上。原文档特别强调在示例中借用检查器知道a.b与a.c是不同的、可以独立借用它不会试图借走整个a——否则这个模式将毫无用处。这是理解该模式的前提分解之所以能生效正是因为编译器已经具备精细的借用粒度只是函数签名把粒度又放大回了整个结构体。何时避免使用让模式回归工具而非教条综合原文档的缺点与讨论部分可以整理出几条务实的边界小结构体语义站不住脚时如果拆出来的Timeout(u32)、PoolSize(u32)并不能构成自洽的领域概念拆分只是给代码增加噪音——此时应当重新审视整体设计而不是执着于拆分。借用冲突并不频繁时如果结构体很少被整体借用、也很少需要并行可变访问引入多个新类型属于过度设计违背仓库 patterns/index.md 中反复强调的 YAGNIYou Arent Going to Need It原则。小结构体确实应该独立存在时那么你也许要的不仅仅是分解以绕过借用检查器而是真正的类型重构——让这些类型成为独立的实体并贯穿整个 API。此时可以参考仓库中的 Newtype 模式newtype.md用带类型语义的包装结构体如ConnectionString之于String同时获得类型安全和借用粒度两方面的收益。从当前仓库的文档组织看结构型模式家族内部存在一条清晰的设计主线small-crates.md 主张小且专一的单元unsafe-mods.md 主张把危险操作关进最小模块而本模式主张把结构拆成可独立借用的最小单元——三者殊途同归粒度更小的单元组合起来反而更自由、更安全、更易维护。实战建议小结先定位冲突根源报错时区分字段级借用冲突与整结构体借用冲突。如果函数只用到少数字段优先考虑修改函数签名按需传字段/小结构体而非克隆或拆结构。拆分的单元要有领域语义像Timeout、PoolSize这样有明确含义的字段才值得独立成类型纯粹为拆而拆会产生异味。善用派生 traitCopy类型传值零开销Clone类型按需复制配合分解模式可彻底消除借用重叠。对照反模式自查如果重构过程中发现自己想写x.clone()来骗过编译器停下来——仓库 borrow_clone.md 正是针对这种情况的警示必要时用cargo clippy检查不必要的克隆。保持克制模式是解决问题的工具不是装饰。当拆分带来的啰嗦超过它解决的借用问题说明该重新考虑设计了。参见本文主体结构体分解以支持独立借用原文档结构性模式导览Structural Patterns反面对照Clone to satisfy the borrow checkerClone 反模式同族模式Prefer small crates、Contain unsafety in small modules配套模式Newtype 模式模式总览与 YAGNI 原则Design Patterns赞分享文档教程【免费下载链接】patternsA catalogue of Rust design patterns, anti-patterns and idioms项目地址https://gitcode.com/gh_mirrors/pa/patterns点击查看免费下载相关推荐Rust 借用检查器实战用 rust-by-practice 的 Borrowing 练习驯服 E0502 借用错误Rust 借用检查器实战用 rust by practice 的 Borrowing 练习驯服 E0502 借用错误 借用Borrowing是 Rust文档教程示例工程Rust 借用检查器Borrow Checker实战解析借用规则、别名约束与编译错误修复 —— 基于 comprehensive-rust 课程Rust 借用检查器Borrow Checker实战解析借用规则、别名约束与编译错误修复 —— 基于 comprehensive rust 课程 本篇技术文档教程Comprehensive Rust 借用与所有权实战Wizards Inventory 练习题完整解答与借用检查器冲突剖析Comprehensive Rust 借用与所有权实战Wizards Inventory 练习题完整解答与借用检查器冲突剖析 本文基于 Google And文档教程上一篇深入剖析Eclipse Milo中节点值Null处理机制从异常到优雅解决方案下一篇解决PySCF多线程困境MKL BLAS后端下DFT梯度计算的线程安全优化指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
企业数字化 ERP 产品动态
相关推荐
Moto 中的 AWS App Mesh 模拟实现:AppMeshBackend 已支持 API 全解析与实战指南 Mock测试 【免费下载链接】moto A library that allows you to easily mock out tests based on AWS infrastructure. 项目地址: https://gitcode.com/gh_mirrors/mo/moto 点击查看 免费下载 本文聚焦开源库 Moto 对 AWS App Mesh 服务的本地模拟实现,以… · 2026/9/25 5:29:24
Amazon Polly 与 AWS SDK for JavaScript (v3) 实战指南:文本转语音与浏览器播放 示例工程教程后端 【免费下载链接】aws-doc-sdk-examples Welcome to the AWS Code Examples Repository. This repo contains code examples used in the AWS documentation, AWS SDK Developer Guides, and more. For more information, see the Readme.md file below. 项目地… · 2026/9/25 5:29:12
Eclipse MAT 1.6.1 Windows x64 堆转储深度分析指南 简介:本资源为Eclipse基金会开源的Java内存分析工具Memory Analyzer(MAT)1.6.1正式版Windows客户端,专为Java开发者、性能调优工程师及JVM故障排查人员设计,用于精准识别堆内存泄漏、定位大对象引用链、优化应用内存占… · 2026/9/25 5:29:00
网御星云安全集中管理系统实战:日志接入、告警配置与运维避坑指南 简介:面向网御星云安全集中管理系统的运维人员与安全管理员,这份PDF手册围绕V3.0.7版本,系统梳理了从登录到主页模块的使用方法。内容涵盖产品特点、软件描述、License控制,并重点展开安全等级、24小时安全趋势、服务器状态和设备… · 2026/9/25 5:56:09
网络安全防御能力评价体系框架:量化评分与整改闭环实战指南 简介:《网络安全防御能力评价体系框架》PDF是360政企安全推出的实战化网络安全能力度量与评价方法,面向企业CISO、安全架构师与蓝队评估人员,解决传统等保、ISO27001等体系“看似完善却难以预判真实攻击效果”的痛点。资源为单个PDF文件&… · 2026/9/25 5:56:09
红蓝攻防全景图:一张图掌控攻防演练全流程 简介:这份PPT全景图面向网络安全攻防人员、蓝队防御工程师及企业安全管理者,系统梳理红蓝攻防实战中的攻击面、暴露面识别,边界突破/防护、横向渗透/区域控制、攻陷/强控等关键阶段,并以基础、强化、协同三层保护机制构建综合防御… · 2026/9/25 5:56:09
PaddleSpeech 的 Kaldi 兼容语音特征提取:python_kaldi_features 从原理到实战 人工智能语音音频NLP媒体生成 【免费下载链接】PaddleSpeech Easy-to-use Speech Toolkit including Self-Supervised Learning model, SOTA/Streaming ASR with punctuation, Streaming TTS with text frontend, Speaker Verification System, End-to-End Speech Translation … · 2026/9/25 5:56:03
AI PLC落地实战:确定性与智能的工业融合方法论 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 5:56:03
Plugin4Shell零点击漏洞:AI编程助手插件安全风险与防护指南 1. 从"零点击"说起:Plugin4Shell 到底在讲一件什么事先把结论摆在前面:Plugin4Shell 不是一个具体的软件产品,而是一类针对 AI 编程助手插件体系的漏洞利用思路的统称。它的核心特征在于"零点击"——也就是说,… · 2026/9/25 5:56:03
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37