'static 经常被误解成「永远活着」。这篇用真实的编译器行为纠正它:什么样的值满足 'static 约束、thread::spawn 为什么要求它、有哪些绕开办法,以及 Box::leak 得到 &'static 的真实成本。
源码:
1 | static GLOBAL: &str = "全局";
2 | fn lit() -> &'static str { "字面量" }
3 | fn leaked() -> &'static mut Vec<i32> { Box::leak(Box::new(vec![1, 2, 3])) }
4 | fn main() {
5 | println!("{} {}", GLOBAL, lit());
6 | let v = leaked();
7 | v.push(4);
8 | println!("Box::leak 得到的 &'static mut:{:?}", v);
9 | }
编译:成功
运行(退出码 0):
全局 字面量
Box::leak 得到的 &'static mut:[1, 2, 3, 4] 源码:
1 | fn needs_static<T: 'static + std::fmt::Debug>(t: T) { println!("收到 {:?}", t); }
2 | fn main() {
3 | let owned = String::from("owned");
4 | needs_static(owned); // 拥有数据:满足 'static
5 | needs_static(5); // 整数:满足
6 | needs_static("literal"); // &'static str:满足
7 | }
编译:成功
运行(退出码 0):
收到 "owned"
收到 5
收到 "literal"
源码:
1 | fn needs_static<T: 'static + std::fmt::Debug>(t: T) { println!("收到 {:?}", t); }
2 | fn main() {
3 | let s = String::from("local");
4 | needs_static(&s);
5 | }
编译:失败
error[E0597]: `s` does not live long enough
--> <实验目录>/src/u26c.rs:4:18
|
3 | let s = String::from("local");
| - binding `s` declared here
4 | needs_static(&s);
| -------------^^-
| | |
| | borrowed value does not live long enough
| argument requires that `s` is borrowed for `'static`
5 | }
| - `s` dropped here while still borrowed
|
note: requirement that the value outlives `'static` introduced here
--> <实验目录>/src/u26c.rs:1:20
|
1 | fn needs_static<T: 'static + std::fmt::Debug>(t: T) { println!("收到 {:?}", t); }
| ^^^^^^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0597`. 源码:
1 | use std::thread;
2 | fn main() {
3 | let v = vec![1, 2, 3];
4 | let h = thread::spawn(|| println!("{:?}", v));
5 | h.join().unwrap();
6 | }
编译:失败
error[E0373]: closure may outlive the current function, but it borrows `v`, which is owned by the current function
--> <实验目录>/src/u26d.rs:4:27
|
4 | let h = thread::spawn(|| println!("{:?}", v));
| ^^ - `v` is borrowed here
| |
| may outlive borrowed value `v`
|
note: function requires argument type to outlive `'static`
--> <实验目录>/src/u26d.rs:4:13
|
4 | let h = thread::spawn(|| println!("{:?}", v));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: to force the closure to take ownership of `v` (and any other referenced variables), use the `move` keyword
|
4 | let h = thread::spawn(move || println!("{:?}", v));
| ++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0373`.
源码:
1 | use std::{sync::Arc, thread};
2 | fn main() {
3 | let v = vec![1, 2, 3];
4 | let h = thread::spawn(move || println!("move 之后线程拥有 v:{:?}", v)); // 解法 1:move
5 | h.join().unwrap();
6 | let shared = Arc::new(vec![4, 5, 6]); let s2 = Arc::clone(&shared);
7 | thread::spawn(move || println!("解法 2:Arc 共享 {:?}", s2)).join().unwrap();
8 | let local = vec![7, 8, 9];
9 | thread::scope(|s| { s.spawn(|| println!("解法 3:thread::scope 可以借用局部变量 {:?}", local)); });
10 | }
编译:成功
运行(退出码 0):
move 之后线程拥有 v:[1, 2, 3]
解法 2:Arc 共享 [4, 5, 6]
解法 3:thread::scope 可以借用局部变量 [7, 8, 9]spawn 与线程的更多内容见线程创建与 Join;异步里的 spawn 与 'static 见spawn 与 'static。
源码:
1 | use std::alloc::{GlobalAlloc, Layout, System};
2 | use std::sync::atomic::{AtomicUsize, Ordering};
3 | struct C; static AL: AtomicUsize = AtomicUsize::new(0); static DE: AtomicUsize = AtomicUsize::new(0);
4 | unsafe impl GlobalAlloc for C {
5 | unsafe fn alloc(&self, l: Layout) -> *mut u8 { AL.fetch_add(1, Ordering::Relaxed); unsafe { System.alloc(l) } }
6 | unsafe fn dealloc(&self, p: *mut u8, l: Layout) { DE.fetch_add(1, Ordering::Relaxed); unsafe { System.dealloc(p, l) } }
7 | }
8 | #[global_allocator] static G: C = C;
9 | fn main() {
10 | let (a0, d0) = (AL.load(Ordering::Relaxed), DE.load(Ordering::Relaxed));
11 | for i in 0..1000 { let b = Box::new([i as u8; 64]); std::hint::black_box(&b); } // 释放
12 | let (a1, d1) = (AL.load(Ordering::Relaxed), DE.load(Ordering::Relaxed));
13 | for i in 0..1000 { let b: &'static mut [u8; 64] = Box::leak(Box::new([i as u8; 64])); std::hint::black_box(&b); } // 泄漏
14 | let (a2, d2) = (AL.load(Ordering::Relaxed), DE.load(Ordering::Relaxed));
15 | println!("1000 个 Box 正常 drop:alloc +{},dealloc +{}", a1 - a0, d1 - d0);
16 | println!("1000 个 Box::leak :alloc +{},dealloc +{}(从不释放)", a2 - a1, d2 - d1);
17 | }
编译:成功
运行(退出码 0):
1000 个 Box 正常 drop:alloc +1000,dealloc +1000
1000 个 Box::leak :alloc +1000,dealloc +0(从不释放)