1010
1111** 【描述】**
1212
13- 对可变静态变量直接进行全局修改是 Unsafe 的。在多线程应用中,修改静态变量会导致数据争用(data race),此未定义行为目前并不会被Clippy或Rustc检测出 。
13+ 对可变静态变量直接进行全局修改是 Unsafe 的。在多线程应用中,修改静态变量会导致数据争用(data race),此未定义行为目前并不会被 Clippy 或 Rustc 检测出 。
1414
1515** 【反例】**
1616
@@ -28,7 +28,7 @@ unsafe fn eat_apple() {
2828
2929** 【正例】**
3030
31- 若需要变更的值的类型为整数或布尔时,可直接使用atomic 。
31+ 若需要变更的值的类型为整数或布尔时,可直接使用 atomic 。
3232
3333``` rust
3434use std :: sync :: atomic :: {AtomicUsize , Ordering :: SeqCst };
@@ -46,31 +46,51 @@ fn eat_apple() {
4646
4747** 【正例】**
4848
49- 若需修改整数或布尔之外的数据类型时,可考虑使用Mutex或Rwlock配合lazy_static宏的方式对全局变量进行变更。
49+ 若需修改整数或布尔之外的数据类型时,可考虑使用 Mutex 或 Rwlock 配合 once_cell 对全局变量进行变更。
50+
51+ (注: once_cell 目前已经被引入到 Nightly 版本的标准库中但还不稳定, 可参考 [ std::lazy] ( https://doc.rust-lang.org/std/lazy/index.html ) 。若要在 Stable 版本下使用,则需要引入第三方库 [ once_cell] ( https://docs.rs/once_cell/latest/once_cell/ ) 。)
5052
5153``` rust
54+ #![feature(once_cell)] // 需要nightly compiler
55+
5256use std :: sync :: Mutex ;
53- use lazy_static :: lazy_static;
57+ use std :: lazy :: SyncLazy ; // 若使用stable版本则需要将之替换为once_cell::sync::Lazy
5458
55- lazy_static! {
56- static ref MESSAGE : Mutex < String > = Mutex :: new (String :: from (" I'm hungry" ));
57- }
59+ static GLOBAL_MESSAGE : SyncLazy < Mutex < String >> = SyncLazy :: new ( || {
60+ Mutex :: new (String :: from (" I'm hungry" ))
61+ });
5862
59- fn update_msg () {
60- let mut old_msg = MESSAGE . lock (). unwrap ();
61- * old_msg = String :: from ( " I'm not hungry anymore! " );
63+ fn update_msg (msg : & str ) {
64+ let mut old_msg = GLOBAL_MESSAGE . lock (). unwrap ();
65+ * old_msg = msg . to_string ( );
6266}
6367
6468fn main () {
65- println! (" {}" , MESSAGE . lock (). unwrap ()); // I'm hungry
66- update_msg ();
67- println! (" {}" , MESSAGE . lock (). unwrap ()); // I'm not hungry anymore!
69+ println! (" {}" , GLOBAL_MESSAGE . lock (). unwrap ()); // I'm hungry
70+ update_msg (" I'm not hungry anymore! " );
71+ println! (" {}" , GLOBAL_MESSAGE . lock (). unwrap ()); // I'm not hungry anymore!
6872}
6973```
7074
75+ 上述示例亦可通过使用第三方库 [ lazy_static] ( https://docs.rs/lazy_static/latest/lazy_static/ ) 的方式实现。
76+
77+ ``` rust
78+ use std :: sync :: Mutex ;
79+ use lazy_static :: lazy_static;
80+
81+ lazy_static! {
82+ static ref GLOBAL_MESSAGE : Mutex <String > = Mutex :: new (String :: from (" I'm hungry" ));
83+ }
84+
85+ fn update_msg (msg : & str ) {
86+ ...
87+ }
88+ ...
89+ ```
90+
7191** 【例外】**
7292
73- 在使用FFI引用外部,例如C的函数时,其本身有可能会返回全局变量。当rust接入这些函数时需要指定输入的变量类型为静态 (static),而若要改变它们的值的时候就需要将其定义为可变静态变量(static mut)。
93+ 在使用FFI引用外部,例如C的函数时,其本身有可能会返回全局变量。当 rust 接入这些函数时需要指定输入的变量类型为静态 (static),而若要改变它们的值的时候就需要将其定义为可变静态变量(static mut)。
7494
7595``` rust
7696use std :: ffi :: CString ;
@@ -85,17 +105,17 @@ fn main() {
85105 let prompt = CString :: new (" [my-awesome-shell] $" ). unwrap ();
86106 unsafe {
87107 rl_prompt = prompt . as_ptr ();
88-
89108 println! (" {:?}" , rl_prompt );
90-
91109 rl_prompt = ptr :: null ();
92110 }
93111}
94112```
95113
96114** 【例外】**
97115
98- 通常情况下直接修改static mut会有线程安全风险,但若配合使用[ std::sync::Once] ( https://doc.rust-lang.org/std/sync/struct.Once.html# ) 则可保证该变量只初始化一次,不会产生线程安全风险。
116+ 通常情况下直接修改 static mut 会有线程安全风险,但若配合使用 [ std::sync::Once] ( https://doc.rust-lang.org/std/sync/struct.Once.html# ) 则可保证该变量只初始化一次,不会产生线程安全风险。
117+
118+ (注:此用法在功能上等同于 [ once_cell::sync::OnceCell] ( https://docs.rs/once_cell/latest/once_cell/sync/struct.OnceCell.html ) 或 Nightly 版本中的 [ std::lazy::SyncOnceCell] ( https://doc.rust-lang.org/std/lazy/struct.SyncOnceCell.html ) 。但在使用 Stable 版本编译器并且不使用第三方库的条件下此写法完全合规,故算作例外情况。)
99119
100120``` rust
101121use std :: sync :: {Mutex , Once };
@@ -127,6 +147,6 @@ fn main() {
127147
128148这条规则如果需要定制 Lint,则应考虑两种情况:
129149
130- 1 . 定义为static mut的变量是否被用于FFI
131- 2 . 定义为static mut的变量在仅被用在call_once或call_once_force等方法的闭包内
150+ 1 . 代码中定义为 static mut 的变量是否仅被用于 FFI
151+ 2 . 代码中定义为 static mut 的变量是否经过 call_once 初始化
132152
0 commit comments