Rust 评分准则

本文档列出了在 Fuchsia 源代码树中编写 Rust 时要遵循的惯例。这些规范综合考虑了最佳实践、项目偏好以及为了保持一致性而做出的一些选择。

指南

命名

大小写符合 Rust 习语

请参阅 C-CASE

临时转化遵循“as_”“to_”和“into_”惯例

请参阅 C-CONV

getter 名称遵循 Rust 惯例

除少数例外情况,Rust 代码中的 getter 不会使用 get_ 前缀。

请参阅 C-GETTER

对生成迭代器的集合上的方法遵循 iteriter_mutinto_iter

请参阅 C-ITER

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

请参阅 C-ITER-TY

名称使用一致的字词顺序

请参阅 C-WORD-ORDER

互操作性

类型热切实现常见 trait

CopyCloneEqPartialEqOrdPartialOrdHashDebugDisplayDefault 都应在适当的时候实现。

请参阅 C-COMMON-TRAITS

转化使用标准特征“From”“AsRef”“AsMut

请参阅 C-CONV-TRAITS

集合会实现 FromIteratorExtend

请参阅 C-COLLECT

数据结构实现了 Serde 的 SerializeDeserialize

请参阅 C-SERDE

尽可能为 SendSync 类型

请参阅 C-SEND-SYNC

错误类型有意义且表现良好

请参阅 C-GOOD-ERR

二进制数字类型提供 HexOctalBinary 格式

请参阅 C-NUM-FMT

通用读取器/写入器函数按值接受 R: ReadW: 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

只有智能指针实现 DerefDerefMut

请参阅 C-DEREF

构造函数是固有的静态方法

请参阅 C-CTOR

灵活应对

函数会公开中间结果以避免重复工作

请参阅 C-INTERMEDIATE

调用方决定复制和放置数据的位置

请参阅 C-CALLER-CONTROL

函数通过使用泛型来最大限度地减少对参数的假设

请参阅 C-GENERIC

如果特征可用作特征对象,则特征是对象安全的

请参阅 C-OBJECT

类型安全

新类型具有静态区别

请参阅 C-NEWTYPE

参数通过类型(而不是 boolOption)传达含义

请参阅 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 的指南: