静态项

语法
StaticItem :
   ItemSafety?1 static mut? IDENTIFIER : Type ( = Expression )? ;

1

safeunsafe 函数修饰符在语义上只允许用于 extern 块内。

静态项类似于常量,不同之处在于它代表程序中一个已分配的对象,该对象通过初始化表达式进行初始化。所有指向该静态项的引用和裸指针都指向同一个已分配的对象。

静态项具有 static 生命周期,它比 Rust 程序中的所有其他生命周期都长。静态项在程序结束时不会调用drop

如果 static 的大小至少为 1 字节,则此已分配对象与所有其他此类 static 对象以及堆分配和栈分配的变量互不相交。然而,不可变的 static 项的存储可以与本身没有唯一地址的对象(例如promotedsconst)重叠。

静态声明在它所在的模块或块的值命名空间中定义一个静态值。

静态初始化器是在编译时计算的常量表达式。静态初始化器可以引用和读取其他静态项。当读取可变静态项时,它们读取该静态项的初始值。

包含非内部可变类型的非 mut 静态项可以被放置在只读内存中。

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

  • 类型必须具有Sync trait bound,以允许线程安全访问。

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

safeunsafe 修饰符在语义上只允许在外部块中使用。

静态项与泛型

在泛型作用域(例如在 blanket 或 default 实现中)中定义的静态项将仅定义一个静态项,就像将静态定义从当前作用域拉到模块中一样。不会每个单态化都有一个项。

这段代码

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;

// This violates the idea of no shared state, and this doesn't internally
// protect against races, so this function is `unsafe`
unsafe fn bump_levels_unsafe() -> u32 {
    unsafe {
        let ret = LEVELS;
        LEVELS += 1;
        return ret;
    }
}

// As an alternative to `bump_levels_unsafe`, this function is safe, assuming
// that we have an atomic_add function which returns the old value. This
// function is safe only if no other code accesses the static in a non-atomic
// fashion. If such accesses are possible (such as in `bump_levels_unsafe`),
// then this would need to be `unsafe` to indicate to the caller that they
// must still guard against concurrent access.
fn bump_levels_safe() -> u32 {
    unsafe {
        return atomic_add(&raw mut LEVELS, 1);
    }
}
}

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

使用静态项或常量项

何时使用常量项或静态项可能会令人困惑。一般来说,应优先使用常量项而不是静态项,除非以下情况之一为真

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