本文档列出了在 Fuchsia 源代码树中编写 Rust 时要遵循的惯例。这些规范综合考虑了最佳实践、项目偏好以及为了保持一致性而做出的一些选择。
指南
命名
大小写符合 Rust 习语
请参阅 C-CASE。
临时转化遵循“as_
”“to_
”和“into_
”惯例
请参阅 C-CONV。
getter 名称遵循 Rust 惯例
除少数例外情况,Rust 代码中的 getter 不会使用 get_
前缀。
请参阅 C-GETTER。
对生成迭代器的集合上的方法遵循 iter
、iter_mut
、into_iter
请参阅 C-ITER。
迭代器类型名称与生成它们的方法相匹配
请参阅 C-ITER-TY。
名称使用一致的字词顺序
请参阅 C-WORD-ORDER。
互操作性
类型热切实现常见 trait
Copy
、Clone
、Eq
、PartialEq
、Ord
、PartialOrd
、Hash
、Debug
、Display
、Default
都应在适当的时候实现。
请参阅 C-COMMON-TRAITS。
转化使用标准特征“From
”“AsRef
”“AsMut
”
请参阅 C-CONV-TRAITS。
集合会实现 FromIterator
和 Extend
请参阅 C-COLLECT。
数据结构实现了 Serde 的 Serialize
、Deserialize
请参阅 C-SERDE。
尽可能为 Send
和 Sync
类型
请参阅 C-SEND-SYNC。
错误类型有意义且表现良好
请参阅 C-GOOD-ERR。
二进制数字类型提供 Hex
、Octal
、Binary
格式
请参阅 C-NUM-FMT。
通用读取器/写入器函数按值接受 R: Read
和 W: Write
请参阅 C-RW-VALUE。
宏
输入语法能够调用输出
请参阅 C-EVOCATIVE。
宏与属性完美搭配
请参阅 C-MACRO-ATTR。
商品宏可在允许包含商品的任何环境中使用
请参阅 C-ANYWHERE。
商品宏支持可见性说明符
请参阅 C-MACRO-VIS。
类型片段很灵活
请参阅 C-MACRO-TY。
文档
crate 级别的文档很详尽,并且包含示例
请参阅 C-CRATE-DOC。
所有项都有一个 rustdoc 示例
请参阅 C-EXAMPLE。
示例使用的是 ?
,而非 try!
,而非 unwrap
请参阅 C-QUESTION-MARK。
函数文档包含错误、应急和安全注意事项
请参阅 C-FAILURE。
散文包含指向相关内容的超链接
请参阅 C-LINK。
Rustdoc 未显示无用的实现细节
请参阅 C-HIDDEN。
每个 unsafe
代码块都有一个随附的理由
安全理由应以 // SAFETY:
开头,并说明为何不安全代码块是合理的。
正确做法
// SAFETY: <why this unsafe operation's safety requirements are met>
错误做法
// Safety: <...>
// [SAFETY] <...>
// <...>
// SAFETY: Trust me.
不安全代码应说明不安全代码块的原因是什么,以及代码块中包含的代码为何是可靠的。如果有看似合适的安全替代方案,但却无法使用,则还应记录无法使用这些替代方法的原因。
正确做法
// SAFETY: The `bytes` returned from our string builder are guaranteed to be
// valid UTF-8. We used to call `from_utf8`, but this caused performance issues
// with large inputs.
let s = unsafe { String::from_utf8_unchecked(bytes) };
错误做法
// SAFETY: We shouldn't have to validate `bytes`, and the safe version is slow.
let s = unsafe { String::from_utf8_unchecked(bytes) };
理由应直接阐明操作要求。只要满足所有要求,可以进行总结。
正确做法
// SAFETY: The caller has guaranteed that `ptr` is valid for reads, properly
// aligned, and points to a properly-initialized `T`.
unsafe {
let x = ptr.read();
}
正确做法
// SAFETY: The caller has guaranteed that `ptr` points to a valid `T`.
unsafe {
let x = ptr.read();
}
错误做法
// SAFETY: `ptr` is safe to read.
unsafe {
let x = ptr.read();
}
安全理由应该说明操作是合理的,而不仅仅是说明操作合理的原因。
正确做法
const BUFFER_LEN: usize = 1024;
fn partially_init(n: usize) -> MaybeUninit<[i32; BUFFER_LEN]> {
// These asserts ensure our safety conditions are met later on
const _: () = assert!(BUFFER_LEN <= 1024);
assert!(n < BUFFER_LEN);
let mut buffer = MaybeUninit::<[i32; BUFFER_LEN]>::uninit();
let ptr = buffer.as_mut_ptr().cast::<i32>();
for i in 0..n {
// SAFETY:
// - `ptr` points to the first `i32` of `buffer`.
// - `buffer` has space for BUFFER_LEN elements and we asserted that
// `n < BUFFER_LEN`.
// - We asserted that `BUFFER_LEN <= 1024`, so `size_of::<i32>() * i`
// is at most 4096 which is less than `isize::MAX` and `usize::MAX`.
let element = unsafe { &mut *ptr.add(i) };
*element = i as i32;
}
buffer
}
错误做法
const BUFFER_LEN: usize = 1024;
fn partially_init(n: usize) -> MaybeUninit<[i32; BUFFER_LEN]> {
// Why are these asserts here?
const _: () = assert!(BUFFER_LEN <= 1024);
assert!(n < BUFFER_LEN);
let mut buffer = MaybeUninit::<[i32; BUFFER_LEN]>::uninit();
let ptr = buffer.as_mut_ptr().cast::<i32>();
for i in 0..n {
// SAFETY:
// - `ptr` is in bounds or one byte past the end of an allocated object.
// - `ptr + i` is also in bounds.
// - The computed offset, in bytes, doesn't overflow an `isize`.
// - The offset being in bounds does not rely on "wrapping around" the
// address space.
let element = unsafe { &mut*ptr.add(i) };
*element = i as i32;
}
buffer
}
已记录不安全的特征,并合理地暗示不安全特征
在记录不安全特征时,应遵循与不安全函数相同的准则。
不安全的特征定义应记录安全注意事项(请参阅 C-FAILURE),并且不安全的特征实现也应该是合理的(请参阅“每个 unsafe
代码块都有一个对应的理由”)
正确做法
/// A labeler that always returns unique labels.
///
/// # Safety
///
/// Every time `create_unique_label()` is called on the same labeler, it must
/// return a distinct `u32`.
unsafe trait UniqueLabeler {
/// Returns a new unique label.
fn create_unique_label(&mut self) -> u32;
}
struct SequentialLabeler {
next: Option<u32>,
}
// SAFETY: `create_unique_label()` will always return the next sequential label
// or panic if all available labels are exhausted.
unsafe impl UniqueLabeler for SequentialLabeler {
fn create_unique_label(&mut self) -> u32 {
if let Some(next) = self.next {
self.next = (next < u32::MAX).then(|| next + 1);
next
} else {
panic!("sequential unique labels exhausted");
}
}
}
错误做法
/// A labeler that always returns unique labels.
unsafe trait UniqueLabeler {
/// Returns a new unique label.
fn create_unique_label(&mut self) -> u32;
}
struct SequentialLabeler {
next: u32,
}
unsafe impl UniqueLabeler for SequentialLabeler {
fn create_unique_label(&mut self) -> u32 {
// This will have correct panicking behavior in debug builds because
// integer overflow is trapped. In a release build, this will still
// overflow but our labeler will not panic!
let result = self.next;
next += 1;
result
}
}
不安全的操作始终位于 unsafe
块中
unsafe
函数在 Fuchsia 中不被视为不安全的上下文。不安全的操作必须始终位于 unsafe
块中,即使它们位于 unsafe
函数正文中也是如此。
正确做法
unsafe fn clear_slice(ptr: *mut i32, len: usize) {
assert!(len.checked_mul(mem::size_of::<i32>()).unwrap() < isize::MAX);
// SAFETY:
// - The caller has guaranteed that `ptr` points to `len` consecutive, valid
// i32s and that the data at behind `ptr` is not simultaneously accessed
// through any other pointer.
// - We asserted that the total size of the slice is less than isize::MAX.
let slice = unsafe { slice::from_raw_parts_mut(ptr, len) };
for x in slice.iter_mut() {
*x = 0;
}
}
错误做法
unsafe fn clear_slice(ptr: *mut i32, len: usize) {
// We forgot to assert that the length of the slice is less than isize::MAX!
// If we justified our call to from_raw_parts_mut, we would have been much
// more likely to remember.
let slice = slice::from_raw_parts_mut(ptr, len);
for x in slice.iter_mut() {
*x = 0;
}
}
可预测性
智能指针不会添加固有方法
请参阅 C-SMART-PTR。
系统会针对所涉及的最具体类型提供转化
请参阅 C-CONV-SPECIFIC
具有清晰接收器的函数是方法
请参阅 C-METHOD。
函数不会提取参数
请参阅 C-NO-OUT。
运营商过载并不令人意外
请参阅 C-OVERLOAD。
只有智能指针实现 Deref
和 DerefMut
请参阅 C-DEREF。
构造函数是固有的静态方法
请参阅 C-CTOR。
灵活应对
函数会公开中间结果以避免重复工作
请参阅 C-INTERMEDIATE。
调用方决定复制和放置数据的位置
请参阅 C-CALLER-CONTROL。
函数通过使用泛型来最大限度地减少对参数的假设
请参阅 C-GENERIC。
如果特征可用作特征对象,则特征是对象安全的
请参阅 C-OBJECT。
类型安全
新类型具有静态区别
请参阅 C-NEWTYPE。
参数通过类型(而不是 bool
或 Option
)传达含义
请参阅 C-CUSTOM-TYPE。
一组标志的类型是 bitflags
,而不是枚举类型
请参阅 C-BITFLAG。
构建器支持构建复杂值
请参阅 C-BUILDER。
可靠性
函数验证其参数
请参阅 C-VALIDATE。
析构函数永不失败
请参阅 C-DTOR-FAIL。
可能阻塞的析构函数有替代方法
请参阅 C-DTOR-BLOCK。
可调试性
所有公共类型都会实现 Debug
请参阅 C-DEBUG。
Debug
表示法永不为空
请参阅 C-DEBUG-NONEMPTY。
满足未来需求
密封特征可防范下游实现
请参阅 C-SEALED。
结构体包含私有字段
请参阅 C-STRUCT-PRIVATE。
newtypes 封装实现详情
请参阅 C-NEWTYPE-HIDE。
数据结构不会重复派生的特征边界
请参阅 C-STRUCT-BOUNDS。
更新准则
如需提议添加或修改,请打开 CL 并抄送 fuchsia-rust-api-rubric@google.com
,以确保对其进行审核。根据反馈对提案进行迭代。
解决反馈后,此文件中任何非提案作者的 OWNERS 都可以作为主持人,并将提案移至最后一次通话。辅导员将向 fuchsia-rust-api-rubric@google.com
发送电子邮件,宣布最后一次通话。此提案将开放 7 个日历日以获得反馈。
在最后一个通话结束时,辅导员讨论或解决了相关问题后,教员将对 CL 发表评论,根据审核反馈和讨论做出最终决定。在做出任何有争议的决定时,都应对相关问题进行充分的公开讨论,并应在 CL 注释中包含理由。决策结果也应发送到电子邮件会话。
如果提案被接受,教员将留下 +2 按钮,然后作者可以提交该提案。
待处理主题
待处理主题在 Rust 问题跟踪器组件中进行跟踪。
与上游 Rust API 准则的关系
此评分准则包含 Rust API 指南的大部分内容,但省略了以下官方准则:
- C-FEATURE,因为 Fuchsia 目前不支持 crate 功能。
- C-METADATA,因为 Fuchsia 不维护内部
Cargo.toml
文件。 - C-HTML-ROOT,因为 Fuchsia 目前不会将大多数 Rust 代码发布到
crates.io
。 - C-RELNOTES,因为 Fuchsia 中的大多数 Rust 代码都“位于 HEAD”。
- C-STABLE,因为 Fuchsia 目前不会将大多数 Rust 代码发布到
crates.io
。 - C-PERMISSIVE,因为 Fuchsia 的所有 Rust 代码均遵循 Fuchsia 许可。
其中包含以下特定于 Fuchsia 的指南: