const、static 与 static mut 常被混在一起讲。这篇逐个验证:它们在内存里的位置,const fn 能把计算提前到编译期的证据,static mut 为什么需要 unsafe、2024 edition 又加了什么限制,以及安全的可变全局应该怎么写。
源码:
1 | const LIMIT: u32 = 100;
2 | static GREETING: &str = "hello";
3 | static TABLE: [u32; 4] = [1, 2, 4, 8];
4 | fn main() {
5 | println!("const LIMIT = {LIMIT}, static GREETING = {GREETING}, TABLE[3] = {}", TABLE[3]);
6 | println!("static TABLE 的地址两次相同吗? {}", &TABLE as *const _ as usize == &TABLE as *const _ as usize);
7 | println!("static TABLE 地址 = {:#x}(在可执行文件的数据段)", &TABLE as *const _ as usize);
8 | let main_addr = main as *const () as usize;
9 | println!("main 函数地址 = {:#x}(代码段)", main_addr);
10 | println!("栈上局部变量地址 = {:#x}", &main_addr as *const usize as usize);
11 | }
编译:成功
运行(退出码 0):
const LIMIT = 100, static GREETING = hello, TABLE[3] = 8
static TABLE 的地址两次相同吗? true
static TABLE 地址 = 0x104b4ee54(在可执行文件的数据段)
main 函数地址 = 0x104b189cc(代码段)
栈上局部变量地址 = 0x16b2e67d8fib(20) 作为 const 求值,get() 的汇编: mov w0, #6765 ; ret 同样的循环在运行时算(runtime(n),16 条指令): cbz w0, LBB1_4 ; mov x9, #0 ; mov w8, #1 ; LBB1_2: ; add x10, x9, x8 ; mov x1, x8 ; mov x9, x8 ; mov x8, x10 ...
编译期断言就是这个机制的应用,见编译期断言。
源码:
1 | static mut COUNTER: u32 = 0;
2 | fn main() {
3 | COUNTER += 1;
4 | println!("{}", COUNTER);
5 | }
编译:失败
error[E0133]: use of mutable static is unsafe and requires unsafe block
--> <实验目录>/src/u13c.rs:3:5
|
3 | COUNTER += 1;
| ^^^^^^^ use of mutable static
|
= note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
error[E0133]: use of mutable static is unsafe and requires unsafe block
--> <实验目录>/src/u13c.rs:4:20
|
4 | println!("{}", COUNTER);
| ^^^^^^^ use of mutable static
|
= note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0133`.
源码:
1 | static mut COUNTER: u32 = 0;
2 | fn main() {
3 | unsafe { COUNTER += 1; }
4 | let v = unsafe { COUNTER };
5 | println!("{}", v);
6 | unsafe { println!("{}", COUNTER); }
7 | }
编译:失败
error: creating a shared reference to mutable static
--> <实验目录>/src/u13d.rs:6:29
|
6 | unsafe { println!("{}", COUNTER); }
| ^^^^^^^ shared reference to mutable static
|
= note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives
= note: for more information, see <https://doc.rust-lang.org/edition-guide/rust-2024/static-mut-references.html>
= note: `#[deny(static_mut_refs)]` (part of `#[deny(rust_2024_compatibility)]`) on by default
error: aborting due to 1 previous error 源码:
1 | use std::sync::atomic::{AtomicUsize, Ordering};
2 | use std::thread;
3 | static COUNTER: AtomicUsize = AtomicUsize::new(0);
4 | fn main() {
5 | let hs: Vec<_> = (0..4).map(|_| thread::spawn(|| { for _ in 0..1000 { COUNTER.fetch_add(1, Ordering::Relaxed); } })).collect();
6 | for h in hs { h.join().unwrap(); }
7 | println!("4 个线程各加 1000 次,COUNTER = {}", COUNTER.load(Ordering::Relaxed));
8 | }
编译:成功
运行(退出码 0):
4 个线程各加 1000 次,COUNTER = 4000
源码:
1 | use std::cell::Cell;
2 | static C: Cell<i32> = Cell::new(0);
3 | fn main() { C.set(1); }
编译:失败
error[E0277]: `Cell<i32>` cannot be shared between threads safely
--> <实验目录>/src/u13f.rs:2:11
|
2 | static C: Cell<i32> = Cell::new(0);
| ^^^^^^^^^ `Cell<i32>` cannot be shared between threads safely
|
= help: the trait `Sync` is not implemented for `Cell<i32>`
= note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead
= note: shared static variables must have a type that implements `Sync`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.原子类型与内存顺序见原子类型与内存顺序;Sync 的含义见Send 与 Sync。