跳转到内容

A.5 Rust 命名约定与工程惯例速查

在阅读 Rust 标准库或第三方开源项目时,你会发现一个有趣的现象:虽然编译器没有规定构造函数必须叫什么、也没有强制规定转换方法的名字,但几乎所有 Rust 代码都像出自同一个人之手。

这得益于 Rust 官方制定的 Rust API Guidelines(API 设计指南)。在 Rust 中,方法名的前缀往往直接编码了所有权流向与运行时内存开销——只要扫一眼方法名,甚至不用看函数签名,你就能判断出这次调用会不会分配堆内存、会不会把原变量移走消费掉。

本节汇总了日常开发与审查代码时最高频的五类工程约定。


一、 大小写命名风格(编译器会发警告)

Section titled “一、 大小写命名风格(编译器会发警告)”

与许多只把命名风格当作团队规范的语言不同,Rust 编译器内置了 non_snake_case、non_camel_case_types 等静态检查规则——如果你写错了大小写风格,编译时就会直接收到黄色的 warning:

语法实体 约定风格 示例
类型(struct / enum)、Trait 大驼峰(UpperCamelCase) UserAccount、HttpError、IntoIterator
枚举变体(Enum Variants) 大驼峰(UpperCamelCase) Option::Some、Result::Err、IpAddr::V4
函数、方法、局部变量、结构体字段、模块(mod) 蛇形小写(snake_case) parse_header、user_name、max_len
常量(const)与静态变量(static) 全大写蛇形(SCREAMING_SNAKE_CASE) MAX_CONNECTIONS、DEFAULT_TIMEOUT_MS
泛型类型参数 单个大写字母(或简短大驼峰) T、E、K、V
生命周期参数 单引号 + 简短小写字母 'a、'de、'static

二、 构造关联函数:new、with_... 与 from_...

Section titled “二、 构造关联函数:new、with_... 与 from_...”

Rust 没有 new Counter() 这样的构造函数语法关键字,所有实例归根结底都通过结构体字面量 Counter { ... } 创建。为了封装内部字段并向调用方提供统一的创建入口,工程上约定俗成使用以下关联函数名:

关联函数名 约定用途 标准库典型示例
Type::new(...) 最默认的无参(或基础参数)构造器 Vec::new()、String::new()、Box::new(val)
Type::with_...(param) 指定特定配置或预分配容量来构造新实例 Vec::with_capacity(10)、String::with_capacity(64)
Type::from_...(src) 从特定格式或底层原材料构造新实例 String::from_utf8(bytes)、u32::from_be_bytes(arr)
Type::try_new(...) / try_from_... 构造过程可能失败(需校验参数合法性),返回 Result<Self, Error> 工程自定义类型如 Email::try_new(raw)

当你为一个自定义结构体提供了无参的 new() 关联函数时,工程上通常还会顺手为它实现标准库的 Default Trait(或者直接在结构体上方标注 #[derive(Default)]),以便其他泛型容器能够自动构造默认值。


三、 类型转换三前缀:as_、to_ 与 into_

Section titled “三、 类型转换三前缀:as_、to_ 与 into_”

这是 Rust 最核心、也最值得牢记的命名契约。当一个方法要把当前类型变成另一种类型的视图或值时,方法名的前缀直接标明了接收者类型(&self 还是 self)以及运行时的内存成本:

方法前缀 接收者签名 所有权流向 内存分配成本 标准库典型示例
as_... &self(或 &mut self) 只读借用(原变量继续可用) 零成本(Free):只构造一个借用视图或胖指针,不复制底层数据 String::as_str(&self) -> &str
String::as_bytes(&self) -> &[u8]
Vec::as_slice(&self) -> &[T]
to_... &self 只读借用(原变量继续可用) 有成本(Expensive):在保留原值不变的前提下,通常需要在堆上重新分配内存并深拷贝或转换数据 str::to_string(&self) -> String
str::to_lowercase(&self) -> String
[T]::to_vec(&self) -> Vec<T>
into_... self 移交所有权(原变量当场失效) 零额外分配或直接拆解复用:把原实例拆开,直接把内部已有的堆缓冲区移交给新类型 String::into_bytes(self) -> Vec<u8>
Vec::into_boxed_slice(self) -> Box<[T]>

四、 Getter 与布尔判断方法:不加 get_ 前缀

Section titled “四、 Getter 与布尔判断方法:不加 get_ 前缀”

从 Java 或 C++ 转过来的开发者,习惯给每个字段写 get_name(&self)、get_age(&self)。但在 Rust 的工程惯例中,访问字段的方法一律直接以字段名命名,不加 get_ 前缀:

  • 普通读取方法:直接写 fn name(&self) -> &str、fn count(&self) -> u32(如果同时提供返回可变引用的方法,则成对命名为 fn name_mut(&mut self) -> &mut String)。
  • 为什么标准库里偶尔会有 get? 只有当访问动作「可能越界或找不到目标、需要返回 Option」时,才会取名为 get——例如切片的按索引安全查找 slice.get(0) -> Option<&T>,以及哈希表的按键查找 map.get("key") -> Option<&V>。
  • 布尔状态判断方法:统一使用 is_... 或 has_... 前缀——例如 v.is_empty()、opt.is_some()、res.is_ok()、char.is_ascii()。

五、 集合遍历三方法:iter、iter_mut 与 into_iter

Section titled “五、 集合遍历三方法:iter、iter_mut 与 into_iter”

几乎所有标准库集合(如 Vec<T>、数组、切片、HashMap)都遵循同一套迭代器方法命名约定,同样精准对应接收者三件套:

方法名 接收者签名 每次迭代产出的元素类型 原集合在遍历后的状态
.iter() &self 只读引用 &T 原集合完好无损,可继续使用
.iter_mut() &mut self 可变引用 &mut T 原集合继续有效,元素已被原地修改
.into_iter() self 拥有所有权的值 T 原集合被拆解消费,遍历后失效

这三套方法与 for 循环语法糖的完整对应关系,将在第三篇第 9 章《迭代器与闭包》中详细拆解。