示例说明
figment 的模型是层叠融合:from 设定基线,merge 逐层覆盖,后合并者优先。Serialized::defaults 把结构体实例直接作为默认层——默认值与类型定义同处维护;Env 的 split 双下划线模式让扁平环境变量映射到嵌套结构:APP_DATABASE__URL 对应 database.url,运维侧无需理解配置内部形状。
技术要点
- 层叠顺序即优先级后压先
- 双下划线映射嵌套配置键
- extract 严格校验并报告层级
代码解读
layered_config 用结构体实例做默认层,依次合并 TOML 文件与 APP 前缀环境变量;extract 把合并结果反序列化为 AppConfig——类型不符或缺失会带位置信息报错。main 设置两个环境变量演示覆盖:environment 变 production、连接池变二十四,其余字段保持下层值。
使用建议
配置文件缺失不报错是 figment 默认行为,关键文件要显式校验存在。密钥走独立 provider 而非环境变量明文。测试里用 Provider trait 注入内存层。
代码示例
use figment::{
providers::{Env, Format, Serialized, Toml},
Figment,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
#[derive(Debug, Serialize, Deserialize, Clone)]
struct DatabaseConfig {
url: String,
pool_size: u32,
connect_timeout_ms: u64,
}
#[derive(Debug, Serialize, Deserialize)]
struct AppConfig {
environment: String,
port: u16,
database: DatabaseConfig,
}
fn layered_config() -> Result<AppConfig, figment::Error> {
let defaults = AppConfig {
environment: "development".into(),
port: 3000,
database: DatabaseConfig {
url: "postgres://localhost/app".into(),
pool_size: 8,
connect_timeout_ms: 3000,
},
};
Figment::from(Serialized::defaults(defaults)) // 内置默认值打底
.merge(Toml::file("config/app.toml")) // 配置文件覆盖
.merge(Env::prefixed("APP_").split("__")) // 环境变量压顶
.extract()
}
fn main() {
// APP_DATABASE__URL 等:双下划线映射嵌套键
std::env::set_var("APP_ENVIRONMENT", "production");
std::env::set_var("APP_DATABASE__POOL_SIZE", "24");
match layered_config() {
Ok(config) => {
println!(
"{} on port {} -> {} (pool {})",
config.environment,
config.port,
config.database.url,
config.database.pool_size
);
let timeout = Duration::from_millis(config.database.connect_timeout_ms);
println!("connect timeout: {timeout:?}");
}
Err(err) => {
// extract 失败会报告具体哪个字段在哪个层出了问题
eprintln!("config error: {err}");
}
}
}