静态项
静态项类似于常量,不同之处在于它表示程序中的一个精确内存位置。对静态项的所有引用都指向同一个内存位置。静态项具有static
生命周期,它比 Rust 程序中的所有其他生命周期都长。静态项在程序结束时不会调用drop
。
静态初始化器是一个在编译时求值的常量表达式。静态初始化器可以引用其他静态项。
包含非内部可变类型的非 mut
静态项可以放置在只读内存中。
对静态项的所有访问都是安全的,但静态项有一些限制
- 该类型必须具有
Sync
特征边界以允许线程安全访问。 - 常量不能引用静态项。
在外部块中,必须省略初始化表达式,而对于自由静态项,则必须提供初始化表达式。
静态项和泛型
在泛型作用域(例如,在 blanket 或默认实现中)中定义的静态项将导致只定义一个静态项,就好像将静态定义从当前作用域中提取到模块中一样。每个单态化将*不会*有一个项。
这段代码
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_unsafe1() -> u32 { let ret = LEVELS; LEVELS += 1; return ret; } // Assuming that we have an atomic_add function which returns the old value, // this function is "safe" but the meaning of the return value may not be what // callers expect, so it's still marked as `unsafe` unsafe fn bump_levels_unsafe2() -> u32 { return atomic_add(&mut LEVELS, 1); } }
可变静态项与普通静态项具有相同的限制,只是该类型不必实现Sync
特征。
使用静态项或常量
您应该使用常量项还是静态项可能会令人困惑。一般来说,应该优先使用常量而不是静态项,除非满足以下条件之一
- 正在存储大量数据
- 需要静态项的单地址属性。
- 需要内部可变性。