静态条目

语法
静态条目 :
   static mut? 标识符 : 类型 ( = 表达式 )? ;

静态条目常量 类似,不同之处在于它表示程序中精确的内存位置。 所有对静态条目的引用都指向同一内存位置。静态条目具有 static 生命周期,其生命周期超过 Rust 程序中的所有其他生命周期。静态条目在程序结束时不会调用 drop 函数。

静态初始化器是一个在编译时计算的 常量表达式 。 静态初始化器可能会引用其他的静态。

mut 的静态条目,如果包含的类型不是 内部可变类型 ,则可以被放置在只读内存中。

所有对静态的访问都是安全的,但是对静态有一些限制:

  • 该类型必须有 Sync trait ,以允许线程安全的访问。
  • 常量不能引用静态。

外部块 中必须省略静态条目的初始化表达式,并且必须为自由静态条目提供初始化表达式。

静态 & 泛型

在泛型作用域中定义的静态条目 (例如在覆盖或默认实现中) 将恰好定义一个静态条目,就好像静态定义从当前作用域被提取到模块中一样。 不会为每个单态化生成一个静态条目。

代码:

use std::sync::atomic::{AtomicUsize, Ordering};

trait Tr {
    fn default_impl() {
        static COUNTER: AtomicUsize = AtomicUsize::new(0);
        println!("default_impl: counter was {}", COUNTER.fetch_add(1, Ordering::Relaxed));
    }

    fn blanket_impl();
}

struct Ty1 {}
struct Ty2 {}

impl<T> Tr for T {
    fn blanket_impl() {
        static COUNTER: AtomicUsize = AtomicUsize::new(0);
        println!("blanket_impl: counter was {}", COUNTER.fetch_add(1, Ordering::Relaxed));
    }
}

fn main() {
    <Ty1 as Tr>::default_impl();
    <Ty2 as Tr>::default_impl();
    <Ty1 as Tr>::blanket_impl();
    <Ty2 as Tr>::blanket_impl();
}

打印

default_impl: counter was 0
default_impl: counter was 1
blanket_impl: counter was 0
blanket_impl: counter was 1

可变静态

如果静态条目用 mut 关键字声明,那么允许程序修改它。 Rust 的一个目标是难以出现并发错误,这显然是导致竞态条件或其他错误的一个很大源头。 因此,在读取或写入可变静态变量时,需要使用 unsafe 块。 应该注意确保对可变静态变量的修改,对与运行在同一进程中的其他线程方面是安全的。

可变静态变量仍然非常有用。它们可以与 C 语言库一起使用,并且还可以在 extern 块中绑定来自 C 语言的库。

#![allow(unused)]
fn main() {
fn atomic_add(_: &mut u32, _: u32) -> u32 { 2 }

static mut LEVELS: u32 = 0;

// 这违反了没有共享状态的理念,而且这在内部也不能防止竞争,所以这个函数是 `unsafe` 的。
unsafe fn bump_levels_unsafe1() -> u32 {
    let ret = LEVELS;
    LEVELS += 1;
    return ret;
}

// 假设我们有一个 atomic_add 函数,返回旧值,
// 这个函数是 "safe" ,但是返回值的含义可能不是调用者所期望的,所以它仍然被标记为 `unsafe` 。
unsafe fn bump_levels_unsafe2() -> u32 {
    return atomic_add(&mut LEVELS, 1);
}
}

可变静态变量与普通静态变量具有相同的限制,只是类型不必实现 Sync trait。

使用静态或常量

在选择使用常量条目或静态条目时可能会感到困惑。 通常情况下,应该优先使用常量而非静态条目,除非以下情况之一:

  • 需要存储大量的数据
  • 需要静态的单地址特性。
  • 需要内部可变性。