Configuração de aplicações Rust: bibliotecas config e dotenv
Qualquer aplicação moderna requer um sistema de configuração flexível: configurações de banco de dados, chaves de API, portas do servidor, níveis de log e outros parâmetros. No ecossistema Rust, existem ferramentas poderosas para isso — as crates config e dotenv. Elas permitem combinar configurações de diferentes fontes (arquivos, variáveis de ambiente, argumentos de linha de comando) em uma única estrutura type-safe.
Por que uma biblioteca separada?
Rust é uma linguagem com tipagem estática forte. A simples leitura de strings de std::env::var() leva a várias verificações e transformações manuais. A biblioteca config automatiza esse processo:
- Suporte a formatos: YAML, JSON, TOML, HJSON, INI, RON
- Estrutura hierárquica de configurações com possibilidade de sobrescrita
- Desserialização automática em estruturas personalizadas via
serde - Combinação de múltiplas fontes com prioridades (arquivo < env < CLI)
Instalação
Adicione ao Cargo.toml:
[dependencies]config = "0.14"serde = { version = "1.0", features = ["derive"] }dotenv = "0.15"Exemplo básico: leitura de arquivo e env
Vamos criar uma estrutura para a configuração da aplicação:
use serde::Deserialize;use config::Config;
#[derive(Debug, Deserialize)]struct AppConfig { server: ServerConfig, database: DatabaseConfig,}
#[derive(Debug, Deserialize)]struct ServerConfig { host: String, port: u16, workers: u8,}
#[derive(Debug, Deserialize)]struct DatabaseConfig { url: String, pool_size: u32,}
fn main() -> Result<(), config::ConfigError> { // Carrega a configuração let config = Config::builder() .add_source(config::File::with_name("config/default")) .add_source(config::Environment::with_prefix("APP")) .build()?;
let app_config: AppConfig = config.try_deserialize()?; println!("{:#?}", app_config); Ok(())}Arquivo config/default.toml:
[server]host = "127.0.0.1"port = 8080workers = 4
[database]url = "postgres://localhost/mydb"pool_size = 10Variáveis de ambiente com o prefixo APP_ sobrescrevem os valores do arquivo. Por exemplo, APP_DATABASE__URL alterará a URL do banco de dados.
Trabalhando com dotenv
Para facilitar o desenvolvimento, é comum usar o arquivo .env. A crate dotenv o carrega nas variáveis de ambiente antes da inicialização do config:
use dotenv::dotenv;
fn main() { dotenv().ok(); // carrega o .env // em seguida, inicialização do config como no exemplo acima}Conteúdo do .env:
APP_SERVER__PORT=3000APP_DATABASE__POOL_SIZE=20Cenário avançado: múltiplas fontes
Projetos reais exigem a combinação de configurações de diferentes lugares. O config permite definir uma cadeia de fontes com prioridade:
let config = Config::builder() // Configurações base (baixa prioridade) .add_source(config::File::with_name("config/default")) // Sobrescrita para um ambiente específico .add_source(config::File::with_name(&format!("config/{}", env::var("APP_ENV").unwrap_or("development")))) // Variáveis de ambiente (prioridade média) .add_source(config::Environment::with_prefix("APP")) // Argumentos de linha de comando (alta prioridade) .add_source(config::File::from(std::env::args().nth(1).unwrap_or_default())) .build()?;Best Practices
- Use prefixos para variáveis de ambiente, para evitar conflitos com variáveis do sistema
Também na biblioteca
Migrações de banco de dados em Java: visão geral de ferramentas e bibliotecas
Configuração de aplicações C++: bibliotecas e ferramentas para personalização flexível
Detekt para Kotlin: guia completo com exemplos
Bibliotecas WebSocket para Node.js: ws, Socket.IO e uWebSockets.js