Rust 評分量表

本文件列出在 Fuchsia 原始碼樹狀結構中編寫 Rust 時應遵循的慣例。這些慣例包含最佳做法、專案偏好以及一些做法,目的是確保一致性。

規範

命名

外殼符合 Rust 慣用語

請參閱 C-CASE

臨時轉換遵循 as_to_into_ 慣例

詳情請參閱 C-CONV

getter 名稱遵循 Rust 慣例

除了少數例外狀況,get_ 前置字串不會用於 Rust 程式碼中的 getter。

詳情請參閱 C-GETTER

產生疊代器的集合方法遵循 iteriter_mutinto_iter

請參閱 C-ITER

Iterator 類型名稱與產生變數的方法相符

請參閱 C-ITER-TY

名稱使用一致的字詞順序

請參閱 C-WORD-ORDER

互通性

積極實作共同特徵的型別

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

類型片段很靈活

詳情請參閱 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 區塊

在 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

特徵若可能做為特徵物件,就可視為物件安全

詳情請參閱 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

新類型會封裝實作詳細資料

請參閱 C-NEWTYPE-HIDE

資料結構不會複製衍生特徵邊界

請參閱 C-STRUCT-BOUNDS

更新規範

如要提出增補或修改要求,請開啟 CL 和副本 fuchsia-rust-api-rubric@google.com,確認評論已送交審查。使用任何 提供意見回饋。

我們處理完意見回饋後,將造成這個檔案的任何擁有者 提案的作者無法擔任講師,並將提案移至最後 呼叫。講師會傳送電子郵件給 fuchsia-rust-api-rubric@google.com 宣布提案最後一則通話提案會開放提供意見 7 天。

在上個通話週期結束時,對於提出相關疑慮後, 上次討論或提及時,講師會在 CL 上 根據審查意見回饋和討論內容做出決定任何爭議性內容 必須充分公開討論相關問題 並且在 CL 註解中說明原因決策結果 電子郵件執行緒

如果提案通過,講師會留下 +2,作者可以 然後提交檔案

待審核的主題

您可以透過 Rust Issue Tracker 元件追蹤待處理主題。

與上游 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 相關規範: