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

转化使用标准特征 FromAsRefAsMut

请参阅 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

类型 fragment 很灵活

请参阅 C-MACRO-TY

文档

crate 级别文档非常全面,并提供了示例

请参阅 C-CRATE-DOC

所有项都有一个 rustdoc 示例

请参阅 C-EXAMPLE

<ph type="x-smartling-placeholder">
</ph>

示例使用 ?,而不是 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
}

不安全的 trait 已记录,不安全的 trait 实现是合理的

不安全特征应按照与不安全函数相同的准则来记录。

不安全特征定义应记录安全注意事项(请参阅 C-FAILURE),以及 不安全的 trait 实现应该是合理的(请参阅 “每个 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 块中

在 Fuchsia 中,unsafe 函数不会被视为不安全的上下文。必须始终确保 位于 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

如果特征作为 trait 对象有用,则它们是对象安全的

请参阅 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

Newtype 封装了实现详情

请参阅 C-NEWTYPE-HIDE

数据结构不会复制派生的特征边界

请参阅 C-STRUCT-BOUNDS

更新指南

要建议添加或修改,请打开 CL 并抄送 fuchsia-rust-api-rubric@google.com 以确保审核通过。使用任意值 以便对提案进行迭代改进。

在反馈得到解决后,该文件的所有者中 而提案作者则不能作为协调员,将提案移到最后 调用。辅导员会向 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 的指南: