简单来说Rust中,全局变量可以分为两种:
- 编译期初始化的全局变量,const创建常量,static创建静态变量,Atomic创建原子类型
- 运行期初始化的全局变量,lazy_static用于懒初始化,Box::leak利用内存泄漏将一个变量的生命周期变为'static。
详细的代码讲解:https://blog.csdn.net/Unicorn_wan/article/details/125099189
// 简单来说,全局变量可以分为两种:
// 编译期初始化的全局变量,const创建常量,static创建静态变量,Atomic创建原子类型
// 运行期初始化的全局变量,lazy_static用于懒初始化,Box::leak利用内存泄漏将一个变量的生命周期变为'static
// 静态变量
const MAX_ID: usize = usize::MAX / 2; // 常量,顾名思义它是不可变的,很适合用作静态配置
// 静态变量
static mut REQUEST_RECV: usize = 0;
// 静态变量不会被内联,在整个程序中,静态变量只有一个实例,所有的引用都会指向同一个地址;存储在静态变量中的值必须要实现 Sync trait
// 原子类型
// 想要全局计数器、状态控制等功能,又想要线程安全的实现,原子类型是非常好的办法。
use std::sync::atomic::{AtomicUsize, Ordering};
static REQUEST_RECV_1: AtomicUsize = AtomicUsize::new(0);
//之前的静态变量都是在编译器初始化的,因此无法使用函数调用进行赋值,而lazy_static允许我们在运行期初始化静态变量!
use std::sync::Mutex;
use lazy_static::lazy_static;
// 为什么需要运行初始化?以下的静态初始化有一个致命的问题:无法用函数进行静态初始化,例如你如果想声明一个全局的Mutex锁
// static NAMES1: Mutex<String> = Mutex::new(String::from("Sunface, Jack, Allen"));
lazy_static! {
static ref NAMES: Mutex<String> = Mutex::new(String::from("Sunface, Jack, Allen"));
}
#[derive(Debug)]
struct Config {
a: String,
b: String,
}
static mut CONFIG: Option<&mut Config> = None;
// Box::leak
// 我们提到了Box::leak可以用于全局变量,例如用作运行期初始化的全局动态配置,先来看看如果不使用lazy_static也不使用Box::leak,会发生什么:
fn test_no_lazy() {
let c = Box::new(Config {
a: "A".to_string(),
b: "B".to_string(),
});
unsafe {
// 报错,Rust 的借用和生命周期规则限制了我们做到这一点,因为试图将一个局部生命周期的变量赋值给全局生命周期的CONFIG,这明显是不安全的。
// CONFIG = Some(&mut Config {
// a: "A".to_string(),
// b: "B".to_string(),
// });
// 将`c`从内存中泄漏,变成`'static`生命周期
CONFIG = Some(Box::leak(c));
println!("{:?}", CONFIG);
}
}
fn main() {
unsafe {
REQUEST_RECV += 1;
// Rust 要求必须使用unsafe语句块才能访问和修改static变量,因为这种使用方式往往并不安全,其实编
//译器是对的,当在多线程中同时去修改时,会不可避免的遇到脏数据
assert_eq!(REQUEST_RECV, 1);
}
for _ in 0..100 {
REQUEST_RECV_1.fetch_add(1, Ordering::Relaxed);
}
println!("当前用户请求数{:?}",REQUEST_RECV_1);
//lazy_static直到运行到main中的第一行代码时,才进行初始化,非常lazy static
let mut v = NAMES.lock().unwrap();
v.push_str(", Myth");
println!("{}",v);
}