Rust:如何在运行时读取配置文件并存储在可跨线程访问的全局结构中?

7
这是我在Rust中的第一个项目,我觉得我错过了一些简单的东西。
我试图创建一个简单的Web API守护进程,它将接收JSON的POST请求,解析JSON并使用配置文件中提供的凭据发送电子邮件。这个问题的90%都很容易。我正在努力解决“在运行时解析配置文件”的问题。
我成功地使用hyper和letter接收JSON并发送电子邮件。但我希望这个守护进程可以在服务器上配置,而不是在构建时(像大多数Linux / Unix守护进程一样)。我已经认真地遵循了这里
我创建了一个配置模块,声明了一个结构体,并使用lazy_static!{}存储配置结构的初始版本。
我认为我已经将我的问题归纳为一个核心问题:如何读取和解析配置文件,然后将值“克隆”到我的结构体中?特别是考虑到这些值的大小在运行时是未知的...
例如:src/config.rs
use std::sync::RwLock;
use serde::Deserialize;
use std::fs;
use std::io::prelude::*;

#[derive(Debug, Deserialize, Clone, Copy)]
pub struct RimfireCfg {
    pub verbose: u8,

    /* web api server config */
    pub listen_address: &'static str,

    /* mail server config */
    pub mailserver: &'static str,
    pub port:                u16,
    pub user:       &'static str,
    pub password:   &'static str,
}

lazy_static! {
    pub static ref CONFIG: RwLock<RimfireCfg> = RwLock::new(
        RimfireCfg {
            verbose: 0,
            listen_address: "127.0.0.1:3000",
            mailserver: "smtp-mail.outlook.com",
            port: 587,
            user: "",
            password: "",
        }
    );
}

impl RimfireCfg {
    pub fn init() -> Result<(), i32> {
        let mut w = CONFIG.write().unwrap();

        /* read the config file */
        let _lcfg: RimfireCfg =
            toml::from_slice(&fs::read("rimfire.toml").unwrap()).unwrap();

        // this is clearly wrong ...
        *w.listen_address = _lcfg.listen_address.clone();
        dbg!(*w);

        Ok(())
    }

    pub fn clone_config() -> RimfireCfg {
        let m = CONFIG.read().unwrap();

        *m
    }
}

src/main.rs

#[macro_use]
extern crate lazy_static;

mod config;

use config::RimfireCfg;

fn main() {
    let a = RimfireCfg::clone_config();

    dbg!(a);

    RimfireCfg::init().unwrap();

    let a = RimfireCfg::clone_config();

    dbg!(a);
}

任何想法?建议?

3
如果你最终要克隆配置文件,为什么不直接按照正常方式构建并在线程间共享呢?至于配置解析本身,这取决于你使用的格式,你可以自己编写解析函数,也可以使用类似 toml 的 crate 来完成解析。我看到你已经在使用 serde, toml 也依赖它 :) 既然你似乎只需要写一次,没有理由不在主函数中构造一个 Arc<Config>,通过克隆 Arc 而不需要任何锁来以不可变方式共享它。 - user11877195
3
@Sahsahae,您能把这个转化成答案吗? - Angelo Fuchs
1个回答

0
你的结构体应存储字符串(String)。字符串是可变的和可扩展的,你可以将数据从文件读入它们,而不像&'static str。&'static str通常仅用于常量字符串字面值。我认为使用String应该能够以最小的调整使你的例子工作。
另外,你可以使用std::mem::swap()一次性写入整个配置。这也更有效率,因为它不需要克隆字符串。
如果你想让配置成为全局变量,我认为使用全局静态锁RwLock是合理的。另外,如Sahsahae的评论所述,你可以避免全局状态,并将Arc传递到使用它的任何地方。这通常更清晰。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接