The config Library for Managing Configuration in Rust
When developing applications in Rust, flexible management of settings is an important task. The config library (the config crate) provides a powerful and convenient way to work with configuration files, environment variables, and built-in values. It supports TOML, JSON, YAML, HJSON, INI formats and allows merging settings from multiple sources with priorities.
Installation
Add the dependency to Cargo.toml:
[dependencies]config = "0.14"serde = { version = "1.0", features = ["derive"] }Basic Usage
Let's create a simple configuration file Settings.toml:
[server]host = "127.0.0.1"port = 8080
[database]url = "postgres://user:pass@localhost/mydb"pool_size = 10Now load it into a Rust application:
use config::{Config, File, Environment};use serde::Deserialize;
#[derive(Debug, Deserialize)]struct ServerConfig { host: String, port: u16,}
#[derive(Debug, Deserialize)]struct DatabaseConfig { url: String, pool_size: u32,}
#[derive(Debug, Deserialize)]struct AppConfig { server: ServerConfig, database: DatabaseConfig,}
fn main() -> Result<(), config::ConfigError> { let settings = Config::builder() .add_source(File::with_name("Settings")) .add_source(Environment::with_prefix("APP")) .build()?;
let app_config: AppConfig = settings.try_deserialize()?; println!("Server: {}:{}", app_config.server.host, app_config.server.port); println!("Database: {}", app_config.database.url); Ok(())}Support for Multiple Formats
The library automatically detects the format based on the file extension. Example with JSON:
let settings = Config::builder() .add_source(File::with_name("config.json")) .add_source(File::with_name("override.yaml").required(false)) .build()?;Environment Variables
Use a prefix for environment variables to override settings without modifying files:
// Run: APP_SERVER__PORT=9090 cargo runlet settings = Config::builder() .add_source(File::with_name("Settings")) .add_source(Environment::with_prefix("APP").separator("__")) .build()?;Nested Settings and Arrays
Hierarchical structures are supported:
#[derive(Debug, Deserialize)]struct AppConfig { features: Vec<String>, logging: LoggingConfig,}
#[derive(Debug, Deserialize)]struct LoggingConfig { level: String, file: Option<String>,}Example TOML with arrays:
features = ["auth", "caching", "metrics"]
[logging]level = "info"file = "/var/log/app.log"Merging Configurations
The library allows layering settings from different sources. Later sources have higher priority:
let settings = Config::builder() .add_source(File::with_name("default")) // base settings .add_source(File::with_name("production").required(false)) // production .add_source(Environment::with_prefix("APP")) // environment variables .build()?;Error Handling and Default Values
Use get() to retrieve individual values with a fallback:
let port: u16 = settings.get("server.port").unwrap_or(8080);let debug: bool = settings.get("debug").unwrap_or(false);Dynamic Access to Settings
If the configuration structure is unknown in advance, use dynamic access:
let val = settings.get_string("database.url")?;let timeout = settings.get_int("timeout")