虽然全局并不一定是个必须的场景,但关于这一点的实践有点意思。
前言
我想在开局从文件中读取配置,作为全局变量。这在其它语言里面是一件非常easy的一件事情,但在Rust里变得有点复杂。
我如果想用静态常量,那么必须一开始就初始化,没法从文件读取。
我如果想用变量,那么必须来一套Arc + Mutex,以使得它们能够用于多线程而不会编译报错。
但是明明知道配置读取以后就不会变化,我们在读取的时候还要去加锁去竞争,显然效率非常低下。
一种实现思路
先随意初始化静态常量,然后通过unsafe
操作改变相应的值,以后正常调用即可。
依赖
[dependencies]
lazy_static = "1.4.0"
实现
// config1.rs
#[derive(Debug)]
pub struct Config<'a> {
pub addr: &'a str,
pub port: u32,
}
lazy_static::lazy_static! {
pub static ref CONFIG: Config<'static> = Config{
addr: "",
port: 0,
};
}
impl Config<'_> {
pub fn global() -> &'static Config<'static> {
&*CONFIG
}
}
pub fn init(addr: &str, port: u32) -> Result<(), std::io::Error> {
let p = std::ptr::addr_of!(*CONFIG) as *mut Config;
unsafe{
std::ptr::write(p, Config{ addr, port});
}
Ok(())
}
初始化
config1::init(&"127.0.0.1", 8080).unwrap();
使用
spawn(|| {
println!("method1 {:#?}", *config1::CONFIG);
println!("method1 {:#?}", *config1::Config::global());
}).join().unwrap();
最佳实践
直接使用第三方依赖即可
依赖
[dependencies]
once_cell = "1.16.0"
实现
// config2.rs
use std::io;
use once_cell::sync::OnceCell;
#[derive(Debug)]
pub struct Config<'a> {
pub addr: &'a str, //最好不要用引用,这里可以使用String
pub port: u32,
}
pub static INSTANCE: OnceCell<Config> = OnceCell::new();
impl Config<'_> {
pub fn global() -> &'static Config<'static> {
INSTANCE.get().expect("Config is not initialized")
}
}
pub fn init(addr: &'_ str, port: u32) -> Result<(), io::Error> {
let conf = Config{addr:"", port};
unsafe{
let p = std::ptr::addr_of!(conf.addr) as *mut &str;
std::ptr::write(p, addr);
}
INSTANCE.set(conf).unwrap();
Ok(())
}
初始化
config2::init(&"127.0.0.1", 8080).unwrap();
使用
spawn(|| {
println!("method1 {:#?}", *config2::Config::global());
}).join().unwrap();