rust - 加载.env并使用辅助函数将其转换为一般结构

标签 rust

我想将多个env.variables转换为静态结构。
我可以手动进行:

 Env {
        is_development: env::var("IS_DEVELOPMENT")
            .unwrap()
            .parse::<bool>()
            .unwrap(),
        server: Server {
            host: env::var("HOST").unwrap(),
            port: env::var("PORT")
                .unwrap()
                .parse::<u16>()
                .unwrap(),
        },
    }
但是,当存在多个值时,它变得肿。有没有一种方法可以使通用帮助程序函数赋予我指定的值或让我感到 panic ?这样的事情(或另一种解决方案):
    fn get_env_var<T>(env_var_name: String) -> T {
        // panic is ok here
        let var = env::var(env_var_name).unwrap(); 

        T::from(var)
    }



    get_env_var<u16>("PORT") // here i got u16
    get_env_var<bool>("IS_DEVELOPMENT") // here is my boolean
完整的例子
use crate::server::logger::log_raw;
use dotenv::dotenv;
use serde::Deserialize;
use std::env;

#[derive(Deserialize, Debug, Clone)]
pub struct Server {
    pub host: String,
    pub port: u16,
}

#[derive(Deserialize, Debug, Clone)]
pub struct Env {
    pub is_development: bool,
    pub server: Server,
}
impl Env {
    pub fn init() -> Self {
        dotenv().expect(".env loading fail");

        // how can i specify what type i expect?
        fn get_env_var<T>(env_var_name: String) -> T {
            // panic is ok here
            let var = env::var(env_var_name).unwrap(); 

            T::from(var)
        }

        // instead this
        Env {
            is_development: env::var("IS_DEVELOPMENT")
                .unwrap()
                .parse::<bool>()
                .unwrap(),
            server: Server {
                host: env::var("HOST").unwrap(),
                port: env::var("PORT")
                    .unwrap()
                    .parse::<u16>()
                    .unwrap(),
            },
        }

        // do something like this
        /*
        Env {
            is_development: get_env_var<bool>("IS_DEVELOPMENT"),
            server: Server {
                host: get_env_var<String>("HOST"),
                port: get_env_var<u16>("PORT"),
            },
        }
        */
    }
}

lazy_static! {
    pub static ref ENV: Env = Env::init();
}

最佳答案

像在使用 str::parse 的手动版本中一样,您可以具有与str::parse(即 FromStr )相同的要求。因此,如果您包括T: FromStr要求,那么您将能够执行var.parse::<T>()

use std::env;
use std::fmt::Debug;
use std::str::FromStr;

fn get_env_var<T>(env_var_name: &str) -> T
where
    T: FromStr,
    T::Err: Debug,
{
    let var = env::var(env_var_name).unwrap();
    var.parse::<T>().unwrap()
}
然后,如果您通过执行PORT=1234 IS_DEVELOPMENT=true cargo run运行以下命令。
fn main() {
    println!("{}", get_env_var::<u16>("PORT"));
    println!("{}", get_env_var::<bool>("IS_DEVELOPMENT"));
}
然后它将输出:
1234
true

或者,您可能希望能够处理 VarError::NotPresent 并回退到默认值。
use std::env::{self, VarError};
use std::fmt::Debug;
use std::str::FromStr;

fn get_env_var<T>(env_var_name: &str) -> Result<T, VarError>
where
    T: FromStr,
    T::Err: Debug,
{
    let var = env::var(env_var_name)?;
    Ok(var.parse().unwrap())
}
现在,如果您仅执行PORT=1234 cargo run,那么这样做会更容易:
let is_dev = get_env_var::<bool>("IS_DEVELOPMENT")
    .map_err(|err| match err {
        VarError::NotPresent => Ok(false),
        err => Err(err),
    })
    .unwrap();
println!("{:?}", is_dev);

如果要回退到 Default ,如果 VarError::NotPresent :
fn get_env_var<T>(env_var_name: &str) -> T
where
    T: FromStr,
    T::Err: Debug,
    T: Default,
{
    let var = match env::var(env_var_name) {
        Err(VarError::NotPresent) => return T::default(),
        res => res.unwrap(),
    };

    var.parse().unwrap()
}

关于rust - 加载.env并使用辅助函数将其转换为一般结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66171718/

相关文章:

rust - 如何接收可选元组返回值

data-structures - 尝试:一次不能多次借用 `_`作为可变项

git - 我可以在 git 存储库中添加一个作为子目录的依赖包吗?

functional-programming - 函数式编程的开销

rust - 您自己的结构派生属性和实现特征之间有什么区别?

json - 如何在循环中将结构序列化为 io::Write

rust - 在另一种支持泛型的类型中使用 impl Trait

mysql - 如何将 Homebrew 安装的 mysql-client 与 diesel-cli 链接起来?

types - Rust 中的默认整数类型是什么?

rust - 如何避免在此结构定义中使用 PhantomData?