静态项
Syntax
StaticItem →
ItemSafety?1 static mut? IDENTIFIER : Type ( = Expression )? ;
静态项类似于常量,只是它表示程序中使用初始化表达式初始化的分配。所有对静态的引用和裸指针都引用相同的分配。
静态项具有 static 生命周期,它比 Rust 程序中的所有其他生命周期都长。静态项在程序结束时不调用 drop。
如果 static 的大小至少为 1 字节,则此分配与所有其他此类 static 分配以及堆分配和栈分配变量是不相交的。但是,不可变 static 项的存储可以与本身没有唯一地址的分配重叠,例如提升和 const 项。
静态声明在其所在的模块或块的值命名空间中定义静态值。
静态初始化器是编译时求值的常量表达式。静态初始化器可以引用和读取其他静态。从可变静态读取时,它们读取该静态的初始值。
包含非内部可变类型的非 mut 静态项可以放在只读内存中。
对静态的所有访问都是安全的,但对静态有一些限制:
- 类型必须具有
Synctrait 约束以允许线程安全访问。
在外部块中必须省略初始化表达式,并且必须为自由静态项提供。
safe 和 unsafe 限定符在语义上仅允许在外部块中使用。
静态与泛型
在泛型作用域中定义的静态项(例如在全面实现或默认实现中)将导致恰好定义一个静态项,就像静态定义从当前作用域拉入模块一样。不会每个单态化有一个项。
此代码:
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。
使用静态或常量
是否应该使用常量项还是静态项可能会令人困惑。一般来说,应该优先使用常量而不是静态,除非以下情况之一为真:
- 存储大量数据。
- 需要静态的单地址属性。
- 需要内部可变性。
-
safe和unsafe函数限定符在语义上仅允许在extern块内使用。 ↩