NiceLeeのBlog 用爱发电 bilibili~

Rust 全局配置的最佳实践

2022-11-09
nIceLee

阅读:


虽然全局并不一定是个必须的场景,但关于这一点的实践有点意思。

前言

我想在开局从文件中读取配置,作为全局变量。这在其它语言里面是一件非常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();

代码

rust-http-proxy-demo


内容
隐藏