Rust RFC 0199 解读:按值、按引用与按可变引用变体的命名约定(ownership variants) 文档【免费下载链接】rfcsRFCs for changes to Rust项目地址https://gitcode.com/gh_mirrors/rf/rfcs点击查看免费下载导读Rust 中同一操作常常存在按值owned按不可变引用borrowed按可变引用mutably borrowed三种变体而早期标准库对这些变体的命名各自为政有时用mut_前缀有时用_mut后缀甚至出现_mut_夹在中间的写法导致 API 极难记忆。本文以 RFC 0199text/0199-ownership-variants.md 为骨架系统讲解这套最终被 Rust 采纳的后缀式命名规则——包括默认不可变借用时的_mut/_move后缀、默认按值时的_ref/_mut后缀以及迭代器的into_iter特例。读完本文你将掌握今天iter/iter_mut/into_iter、as_ref/as_mut、get_ref/get_mut等 API 命名的来源与设计取舍并能把这套约定直接应用到自己的 crate 设计之中。背景为什么需要一套统一约定RFC 0199 是一份conventions RFC约定类 RFC目标是当某个操作存在按值、按引用、按可变引用三种变体时统一其命名方式。在 RFC 提出时2014 年标准库对 mut 变体的标记方式并不一致有时用mut_前缀有时用_mut后缀偶尔还会在中间出现_mut_。这些不一致让 API 难以记忆。虽然每种立场都能找到支持的理由但统一约定本身能带来巨大收益而且某种程度上我们只需要做出一个选择——这正是 RFC 的务实出发点。典型的例子是迭代器方法的三元组iter处理不可变借用数据mut_iter处理可变借用数据move_iter处理按值owned数据。对于迭代器默认不带标记的变体是不可变借用而在其他场景下默认变体往往是按值。RFC 提出的规则取决于哪个变体是默认的但无论哪种情况都统一使用后缀来标记非默认变体。核心规则后缀式命名规则一默认不可变借用immutably borrowed by default如果foo默认使用/产生不可变借用那么可变借用变体使用_mut后缀例如foo_mut按值owned变体使用_move后缀例如foo_move。不过迭代器是一个例外移动变体也可以被理解为一种into转换写成into_iter。对比一下for x in v.into_iter() { ... } // 读起来明显更自然 for x in v.iter_move() { ... } // 生硬因此迭代器场景下的约定是into_iter而非iter_move。注意该约定只覆盖迭代器的方法名不覆盖迭代器类型名。迭代器类型的命名是后续 RFCRFC 0344的主题。规则二默认按值owned by default如果foo默认使用/产生按值数据那么不可变借用变体使用_ref后缀例如foo_ref可变借用变体使用_mut后缀例如foo_mut。例外mut是类型名的一部分对于可变借用变体如果mut限定词本身就是类型名的一部分例如as_mut_slice中的mut_slice那么mut应该按照它在类型中出现的形态来书写即as_mut_slice而不是as_slice_mut。对类型名的引用统一用mut缩写当时的标准库中有的地方写as_ref/as_mut有的地方写get_ref/get_mut_ref。RFC 提议普遍将mut作为mut_ref的缩写统一成as_ref/as_mut、get_ref/get_mut这样的成对形态。设计理由为什么用后缀后缀让同组变体在视觉上更容易归拢尤其是在按字母排序时foo、foo_move、foo_mut、foo_ref会自然排在一起。它把注意力放在功能本身上而不是修饰词上。为什么用move历史上 Rust 用move来表示所有权转移也与 C 的术语体系对接。其主要缺点是它没有强调所有权ownership——而这正是 Rust 当前的主叙事。但从另一个角度看在 Rust 中所有数据都是有所有权的所以用_owned作为限定词反而有点奇怪。Copytrait 对任何所有权转移术语都构成挑战。RFC 给出的心智模型是对于Copy数据你是在移动一份拷贝moving a copy。为什么用mut而不是mut_ref更短且as_ref与as_mut这对名称有一种舒服的和谐感不会让某一种引用显得比另一种更重要。备选方案与取舍备选一前缀或混合限定词给变体加前缀也是一种可能但似乎没什么好处。当时仓库里前缀与后缀混用的情况虽然可以用语法学区分grammatical distinctions来合理化但这种做法过于微妙和复杂需要很强的英语语法功底才能用好因此被否决。备选二无后缀例外一律把限定词放后缀本规则在mut属于类型名如as_mut_slice时破例另一种做法是始终把限定词放在后缀写成as_slice_mut。这会让 API 在某些方面更一致但在另一些方面更不一致——转换函数将不再一致地转录自己的类型名。RFC 认为这或许并不算太糟因为我们本来就经常缩写类型名但无论如何方法中如何引用类型名需要单独的约定 RFC 来规范这一条后来正是由 RFC 0344 完成。备选三用owned代替moveRust 的整体叙事一直在向所有权是核心概念、借用是所有权的一种较弱形式演进因此_owned似乎是个合理选择。但问题是ref变体并没有说 borrowed所以这个选择在某种意义上并不一致而且owned术语对来自 C 背景的开发者不够熟悉。备选四用val代替owned另一个选项是用val或value。这个建议呼应了按引用by reference/按值by value的区分与ref的配套性比move更强缺点是它不如move或owned那么清晰、有画面感。在仓库后续 RFC 中的落地与验证RFC 0199 不是孤立的提案仓库中多个后续 RFC 直接引用并落实了它的约定可以作为我们理解这套规则如何演化的直接证据。迭代器三件套的定型iter/iter_mut/into_iterRFC 0344text/0344-conventions-galore.md 明确写道一个更早的 RFC即本文所述的 RFC 199确定了标准迭代器方法iter、iter_mut、into_iter。同时它把迭代器类型名也统一为方法名去掉 snake_case 后的 PascalCase 形式iter产生Iteriter_mut产生IterMutinto_iter产生IntoIter。这份 RFC 还总结了方法中引用类型名的对照表其中正对应着 RFC 0199 的mut缩写决定类型名方法中的写法strstr[T]slicemut [T]mut_slice[u8]bytesTrefmut Tmut*const Tptr*mut Tmut_ptr并说明这里唯一的意外是使用mut而非mut_ref来表示可变引用。这个缩写已经是相当常见的约定例如as_ref与as_mut方法目的是让这个非常常见的场景保持简短。——这正是 RFC 0199 提案的直接延续。集合 API 的标准三件套RFC 0235text/0235-collections-conventions.md 在迭代一节为每个集合规定了标准迭代方法三元组并要求集合类型同时为自身和可变引用实现IntoIteratorfn iter(a self) - Itemsa; fn iter_mut(a mut self) - ItemsMuta; fn into_iter(self) - ItemsMove;同一 RFC 还给出了IntoIteratortrait 的设计及impl Iterator for I的 blanket 实现并展示了Vec的三个IntoIterator实现VecT、a VecT、a mut VecT。这也印证了 RFC 0199 中移动变体可以理解为into转换的论断——into_iter因为按值消费 self天然只产生单次迭代与 Java 等语言中 iterable 可无限次迭代 的语义形成对照。转换 trait 的成对命名RFC 0529text/0529-conversion-traits.md 把as_ref/as_mut固化为一对基础转换 traittrait AsRefT: ?Sized { fn as_ref(self) - T; } trait AsMutT: ?Sized { fn as_mut(mut self) - mut T; } trait IntoT { fn into(self) - T; } trait FromT { fn from(T) - Self; }并说明前三者镜像了我们的as/into约定as风格的转换是引用到引用into风格的转换是任意类型之间按值消费参数。这正是 RFC 0199统一用mut缩写mut_ref的落地产物。类型名中的mut例外as_mut_sliceRFC 0198text/0198-slice-notation.md 设计切片 trait 时采用了as_mut_slice、slice_from_mut、slice_to_mut、slice_mut等名称并特别注明这里的可变名称是命名约定可能变更的一部分将在一个单独的 RFC 中描述——即指向 RFC 0199 的例外规则当mut是类型名mut slice的一部分时按其在类型中的形态书写。可见 RFC 0199 的例外条款正是为了兼容这类场景而设计的。历史过渡期的证据mut_iter在 RFC 0179text/0179-and-mut-patterns.md 中仍能看到旧命名v.mut_iter()的痕迹这从侧面说明 RFC 0199 提出的后缀式命名iter_mut确实取代了早期的前缀式mut_iter是 Rust 命名体系演进中承前启后的一环。总结与实操建议RFC 0199 确立的命名体系可以浓缩为一张速查表默认变体非默认变体标记默认不可变借用foo可变借用 →foo_mut按值 →foo_move迭代器例外into_iter默认按值foo不可变借用 →foo_ref可变借用 →foo_mutmut属于类型名按类型原样书写如as_mut_slicemut_ref缩写统一为mut如as_ref/as_mut这套约定经过 RFC 0344、RFC 0235、RFC 0529 等一系列后续 RFC 的细化与落地最终成为我们今天熟知的iter/iter_mut/into_iter、as_ref/as_mut、get_ref/get_mut等 API 形态的源头。在设计自己的库时可以遵循同样的判断顺序先确定默认变体是借用还是按值非默认变体一律用后缀标记_mut、_ref、_move/_into_若mut属于类型名的一部分保持其在类型中的形态对于产生迭代器的方法优先使用iter、iter_mut、into_iter三件套。这样写出的 API 既便于按字母序分组浏览也符合 Rust 社区十年来沉淀下来的直觉与记忆模式。赞分享文档【免费下载链接】rfcsRFCs for changes to Rust项目地址https://gitcode.com/gh_mirrors/rf/rfcs点击查看免费下载相关推荐Rust 编译器错误码 E0009 详解同一模式中混用按值与按引用绑定的历史限制rustc_error_codesRust 编译器错误码 E0009 详解同一模式中混用按值与按引用绑定的历史限制rustc_error_codes 本文基于 Rust 编译器仓库中 E0编程语言编译器语言运行时标准库30 seconds of code 深度解读JavaScript 到底是按值传递还是按引用传递30 seconds of code 深度解读JavaScript 到底是按值传递还是按引用传递 导读 在 JavaScript 开发中函数参数究竟如何教程文档Rust RFC 151 解读闭包按值捕获capture by value的设计起源与演进Rust RFC 151 解读闭包按值捕获capture by value的设计起源与演进 导读 本篇文章围绕本仓库中的 RFC 0151text/01文档上一篇高效科研必备Pubmed-Batch-Download批量文献下载工具全解析下一篇终极Vue3后台管理系统从零实现AI驱动的数据可视化面板创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考