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

互通性

積極實作常見特徵的類型

請視情況實作 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

說明文件

收錄層級說明文件的內容非常詳盡,並提供範例

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

類型安全

Newtypes 提供靜態差異

請參閱 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 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 專屬規範: