環境
- macOS 13.1
- rustc 1.65.0
他言語のようには定義できない...
Pythonの場合
constants.py
import os
from dotenv import load_dotenv
load_dotenv()
XXX = os.getenv("XXX")
Rustで書こうとするとPythonのようには書けません。
constants.rs
use std::env::{self, VarError};
pub static XXX: Result<String, VarError> = env::var("xxx");
error[E0015]: cannot call non-const fn `std::env::var::<&str>` in statics
--> src/constants.rs:36:45
|
36 | pub static XXX: Result<String, VarError> = env::var("xxx");
| ^^^^^^^^^^^^^^^
|
= note: calls in statics are limited to constant functions, tuple structs and tuple variants
= note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell
E0015の詳細を読み解くと、定数として定義する場合は初期化する際にconstant expressionを用いた方法でないといけないとあり、env::var("xxx")
はconst functionではないためこのようには定義できません。
これを修正できる案として、エラーメッセージに以下の内容が書いてあります。
= note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell
once_cell
はlazy_static
に取って代わるクレートで、lazy_static
はマクロを用いるのに対し、once_cell
はマクロを用いずに以下のように書けます。
constants.rs
use std::env;
use once_cell::sync::Lazy;
pub static XXX: Lazy<String> = Lazy::new(|| env::var("xxx").unwrap());
一つの変数を読み取るのであれば上記方法をとってもいいかもしれませんが、複数の変数を読み取るのであればConfig
クレートも一緒に使う方が楽かもしれません。
envファイルの内容をグローバル変数として定義する
使用するクレイト
Cargo.toml
dotenv = "0.15.0"
serde = "1.0.151"
config = "0.13.3"
once_cell = "1.17.0"
envファイル
.env
xxx="hello from .env"
コード
constants.rs
use config::ConfigError;
use dotenv::dotenv;
use once_cell::sync::Lazy;
use serde::Deserialize;
#[derive(Deserialize)]
pub struct Config {
// .envファイルから読み取る値をフィールドに定義する
pub xxx: String,
}
impl Config {
// 環境変数よりConfigを作成し、返却する
pub fn from_env() -> Result<Self, ConfigError> {
let cfg = config::Config::builder()
.add_source(config::Environment::default())
.build()?;
cfg.try_deserialize()
}
}
pub static CONFIG: Lazy<Config> = Lazy::new(|| {
dotenv().expect("Cannot find .env file");
Config::from_env().expect("Failed to load config")
});
参考