写了这么久rust,你的变量和函数命名符合社区公约吗?

本文翻译自rust社区的命名公约(Naming

命名公约

一般来说,Rust 倾向于用大驼峰命名法(UpperCamelCase)来命名类名、结构体名、类型名等在编程中表示类或类型的标识符,而蛇形命名法(snake_case)用于命名变量、函数、方法、文件名等在编程中表示标识符的方式。详细情况如下:

ItemConvention
Cratesunclear
Modulessnake_case
TypesUpperCamelCase
TraitsUpperCamelCase
Enum variantsUpperCamelCase
Functionssnake_case
Methodssnake_case
General constructorsnew or with_more_details
Conversion constructorsfrom_some_other_type
Macrossnake_case!
Local variablessnake_case
StaticsSCREAMING_SNAKE_CASE
ConstantsSCREAMING_SNAKE_CASE
Lifetimesshort lowercase, usually a single letter: 'a, 'de, 'src
Type parametersconcise UpperCamelCase, usually single uppercase letter: T
Featuresunclear but see C-FEATURE

UpperCamelCase中,复合词的首字母缩写和缩略词算作一个单词:使用Uuid而不是UUID,使用Usize而不是USize,使用Stdin而不是StdIn。在snake_case中,首字母缩写和缩略词都使用小写:is_xid_start
snake_caseSCREAMING_SNAKE_CASE中,“单词”不应该只包含一个字母,除非它是最后一个“单词”。所以,我们使用btree_map而不是b_tree_map,但是使用PI_2而不是PI2
Crate名称不应该使用-rs-rust作为后缀或前缀。每个crate都是Rust!不需要不断提醒用户这一点,这样做没有意义。

标准库中的示例

下面将介绍一些实例,帮助更加容易得理解公约 。

临时转换命名遵循 as_、to_、into_ 的约定

转换应作为方法提供,名称前缀如下:

前缀代价所有权
as_零成本borrowed -> borrowed
to_开销很大borrowed -> borrowed
borrowed -> owned (non-Copy types)
owned -> owned (Copy types)
into_不确定owned -> owned (non-Copy types)

举个例子:

  • str::as_bytes() 方法将一个 str 视为 UTF-8 字节的切片视图,这是零代价的。输入是一个借用的 &str ,输出是一个借用的 &[u8]
  • Path::to_str() 方法在操作系统路径的字节上执行了一个开销很大的 UTF-8 检查。输入和输出都是借用的。将其命名为 as_str 是不正确的,因为该方法在运行时具有昂贵的成本。
  • str::to_lowercase() 方法生成 str 的 Unicode 正确的小写等价物,这涉及对字符串的字符进行迭代,并可能需要内存分配。输入是一个借用的 &str ,输出是一个拥有的 String
  • f64::to_radians() 方法将一个浮点数从度转换为弧度。输入是 f64 。不需要传递引用 &f64 ,因为 f64 的复制成本很低。将该函数命名为 into_radians 会产生误导,因为输入并没有被消耗。
  • String::into_bytes() 方法提取了 String 的底层 Vec<u8> ,这是零代价的。它接收一个 String 的所有权,并返回一个有所有权的 Vec<u8>
  • BufReader::into_inner() 方法接收一个缓冲读取器的所有权,并提取出底层的读取器,这是零成本的。缓冲区中的数据将被丢弃。
  • BufWriter::into_inner() 方法接收一个缓冲写入器的所有权,并提取出底层的写入器,这可能需要刷新缓冲数据,操作的开销很大。

Getter的命名约定

除了少数例外情况外,在Rust代码中不使用"get_"前缀来命名获取器(getter)。


#![allow(unused)]
fn main() {
pub struct S {
    first: First,
    second: Second,
}

impl S {
    // Not get_first.
    pub fn first(&self) -> &First {
        &self.first
    }

    // Not get_first_mut, get_mut_first, or mut_first.
    pub fn first_mut(&mut self) -> &mut First {
        &mut self.first
    }
}
}

get的命名仅在存在单个明显可通过getter合理获取的对象时使用。例如,Cell::get 用于访问 Cell 的内容。
对于需要进行运行时验证(如边界检查)的getter,可以考虑添加不安全的_unchecked变体。通常,这些变体的签名如下所示。


#![allow(unused)]
fn main() {
fn get(&self, index: K) -> Option<&V>;
fn get_mut(&mut self, index: K) -> Option<&mut V>;
unsafe fn get_unchecked(&self, index: K) -> &V;
unsafe fn get_unchecked_mut(&mut self, index: K) -> &mut V;
}

getter和conversion之间的区别可能是微妙的,而且并不总是明确的。例如,TempDir::path 可以理解为获取临时目录的文件系统路径的 getter,而 TempDir::into_path 是将删除临时目录的责任转移给调用者的转换器。尽管 path 是一个getter,将其称为 get_path 或 as_path 是不正确的。
再看下其他的例子

  • std::io::Cursor::get_mut
  • std::ptr::Unique::get_mut
  • std::sync::PoisonError::get_mut
  • std::sync::atomic::AtomicBool::get_mut
  • std::collections::hash_map::OccupiedEntry::get_mut
  • <[T]>::get_unchecked

集合迭代器命名约定

集合上产生迭代器的方法遵循 iter、iter_mut、into_iter(C-ITER)的命名规则。
对于元素类型为U的容器,迭代器方法的命名应为:


#![allow(unused)]
fn main() {
	fn iter(&self) -> Iter             // Iter implements Iterator<Item = &U>
	fn iter_mut(&mut self) -> IterMut  // IterMut implements Iterator<Item = &mut U>
	fn into_iter(self) -> IntoIter     // IntoIter implements Iterator<Item = U>
}

这个准则适用于在概念上是同质集合的数据结构。以str类型为反例,它是一系列保证为有效UTF-8的字节片段。这个概念上更为微妙,因此不提供iter/iter_mut/into_iter一组迭代器方法,而是提供str::bytes和str::chars,再按照字符数组迭代。
这个准则仅适用于方法,而不适用于函数。例如,来自url crate的percent_encode返回一个迭代器,用于迭代百分比编码的字符串片段。使用iter/iter_mut/into_iter约定并不会增加清晰度。

看下标准库的命名会更加清楚:

  • Vec::iter
  • Vec::iter_mut
  • Vec::into_iter
  • BTreeMap::iter
  • BTreeMap::iter_mut

迭代器类型的命名与生成它们的方法匹配

一个名为into_iter()的方法应该返回一个名为IntoIter的类型,其他返回迭代器的方法也是如此。
这个准则主要适用于方法,但通常也适用于函数。例如,来自url crate的percent_encode函数返回一个名为PercentEncode的迭代器类型。
这些类型名称在加上所属模块的前缀时最有意义,例如vec::IntoIter。

看下标准库的实例

  • Vec::iter 返回Iter
  • Vec::iter_mut 返回IterMut
  • Vec::into_iter 返回IntoIter
  • BTreeMap::keys 返回Keys
  • BTreeMap::values 返回Values

特性名称不包含占位符

在Cargo特性的名称中不要包含没有任何意义的词语,例如use-abc或with-abc。直接将特性命名为abc。
这在Rust标准库有可选依赖的crate中最常见。正确做法是:

 # 在Cargo.toml中
[features]
default = ["std"]
std = []
 // 在lib.rs中
 #![cfg_attr(not(feature = "std"), no_std)]

不要将特性命名为use-std或with-std,也不要使用任何与std无关的创意名称。这个命名约定与Cargo为可选依赖项推断的隐式特性的命名方式一致。考虑一个crate x,它可选择依赖于Serde和Rust标准库:

[package]
name = "x"
version = "0.1.0"
 [features]
std = ["serde/std"]
 [dependencies]
serde = { version = "1.0", optional = true }

当我们依赖于x时,可以通过features = [“serde”]来启用可选的Serde依赖项。类似地,我们可以通过features = [“std”]来启用可选的标准库依赖项。Cargo为可选依赖项推断的隐式特性被称为serde,而不是use-serde或with-serde,因此我们希望显式特性也遵循相同的方式。
作为相关说明,Cargo要求特性是可添加的,因此no-abc这样带有否定意义的特性名称几乎永远都是不正确的。

词序保持一致

以下是一些标准库中的错误类型:

  • JoinPathsError
  • ParseBoolError
  • ParseCharError
  • ParseFloatError
  • ParseIntError
  • RecvTimeoutError
  • StripPrefixError
    所有这些都使用动词-宾语-错误的词序。如果我们要添加一个表示地址解析失败的错误,为了保持一致性,我们应该将其命名为动词-宾语-错误的顺序,例如ParseAddrError,而不是AddrParseError。
    具体的词序选择并不重要,但要注意在crate内保持一致性,并与标准库中类似功能保持一致。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值