靜態和常數變數

靜態和常數變數是建立全域範圍值的兩種不同方式,這個值無法在程式執行期間移動或重新分配。

const

常數變數會在編譯期間評估,且無論用於何處,其值都會內嵌:

const DIGEST_SIZE: usize = 3;
const ZERO: Option<u8> = Some(42);

fn compute_digest(text: &str) -> [u8; DIGEST_SIZE] {
    let mut digest = [ZERO.unwrap_or(0); DIGEST_SIZE];
    for (idx, &b) in text.as_bytes().iter().enumerate() {
        digest[idx % DIGEST_SIZE] = digest[idx % DIGEST_SIZE].wrapping_add(b);
    }
    digest
}

fn main() {
    let digest = compute_digest("Hello");
    println!("Digest: {digest:?}");
}

根據《Rust RFC 手冊》所述,這類值會在使用時內嵌。

您只能在編譯期間呼叫標示為 const 的函式,以便產生 const 值,但可以在執行階段呼叫 const 函式。

static

靜態變數會在程式的整個執行過程中持續運作,因此不會移動:

static BANNER: &str = "Welcome to RustOS 3.14";

fn main() {
    println!("{BANNER}");
}

如《Rust RFC 手冊》所述,這類值在使用時不會內嵌,且具備實際相關聯的記憶體位置。這對不安全和嵌入的程式碼很有幫助,且變數在程式執行全程都會持續運作。當全域範圍值沒有需要物件識別子的理由時,通常首選會是使用 const

由於 static 變數可從任何執行緒存取,因此必須是 Sync。內部可變動性則可透過原子或類似的 Mutex 實現。也可能有可變動的靜態項目,但這些需要手動同步,因此每當存取這類項目時就需要動用 unsafe 程式碼。我們會在「不安全的 Rust」章節中探討可變動的靜態項目

  • 別忘了提到 const 的行為在語意上與 C++ 的 constexpr 相似。
  • 另一方面,static 則更類似於 C++ 中的 const 或可變動的全域變數。
  • static 提供物件識別子,也就是記憶體中的位址,和具有內部可變動性型別 (例如 Mutex<T>) 所需的狀態。
  • 需要在執行階段評估常數的情況雖不常見,但這會比使用靜態項目更有用且安全。
  • 您可以使用 std::thread_local 巨集來建立 thread_local 資料。

屬性表:

資源靜態常數
具備記憶體中的位址否 (已內嵌)
在整個程式執行期間持續存在
可變動是 (不安全)
Evaluated at compile time是 (已在編譯時初始化)
無論在何處使用都會內嵌