Variáveis de ambiente são a forma padrão de configurar aplicações em ambientes de produção, containers Docker e plataformas de deploy. Rust oferece std::env::var na biblioteca padrão, e a crate dotenvy adiciona suporte a arquivos .env para desenvolvimento local. Nesta receita, você vai aprender a ler, validar e organizar variáveis de ambiente em Rust.
Dependências
[package]
name = "receita-env"
version = "0.1.0"
edition = "2021"
[dependencies]
dotenvy = "0.15"
Código Completo
use std::env;
use std::net::SocketAddr;
fn main() {
// =============================================
// 1. env::var() — leitura básica
// =============================================
println!("=== Leitura Básica ===");
// Variáveis do sistema (geralmente disponíveis)
match env::var("HOME") {
Ok(valor) => println!("HOME: {}", valor),
Err(e) => println!("HOME não definida: {}", e),
}
match env::var("PATH") {
Ok(valor) => {
let paths: Vec<&str> = valor.split(':').take(3).collect();
println!("PATH (3 primeiros): {:?}", paths);
}
Err(e) => println!("PATH não definida: {}", e),
}
// Variável que provavelmente não existe
match env::var("MINHA_VARIAVEL_INEXISTENTE") {
Ok(valor) => println!("Valor: {}", valor),
Err(env::VarError::NotPresent) => println!("MINHA_VARIAVEL: não definida"),
Err(env::VarError::NotUnicode(_)) => println!("MINHA_VARIAVEL: valor inválido"),
}
// =============================================
// 2. dotenvy — carregar arquivo .env
// =============================================
println!("\n=== dotenvy (.env) ===");
// Criar um .env de exemplo para demonstração
std::fs::write("/tmp/.env.exemplo", r#"
DATABASE_URL=postgresql://localhost:5432/meudb
SECRET_KEY=minha-chave-super-secreta-2026
APP_PORT=8080
APP_DEBUG=true
APP_HOST=0.0.0.0
LOG_LEVEL=info
REDIS_URL=redis://localhost:6379
MAX_CONEXOES=50
"#.trim()).unwrap();
// Carregar .env (ok se não existir)
match dotenvy::from_path("/tmp/.env.exemplo") {
Ok(_) => println!("Arquivo .env carregado"),
Err(e) => println!("Sem .env: {}", e),
}
// Agora as variáveis estão disponíveis via env::var
if let Ok(url) = env::var("DATABASE_URL") {
println!("DATABASE_URL: {}", url);
}
if let Ok(port) = env::var("APP_PORT") {
println!("APP_PORT: {}", port);
}
// Limpar arquivo de teste
let _ = std::fs::remove_file("/tmp/.env.exemplo");
// =============================================
// 3. Variáveis obrigatórias vs opcionais
// =============================================
println!("\n=== Obrigatórias vs Opcionais ===");
// Obrigatória — panic se não existir (ok para inicialização)
fn env_obrigatoria(nome: &str) -> String {
env::var(nome).unwrap_or_else(|_| {
panic!("Variável de ambiente '{}' é obrigatória", nome)
})
}
// Opcional com valor padrão
fn env_opcional(nome: &str, padrao: &str) -> String {
env::var(nome).unwrap_or_else(|_| padrao.to_string())
}
// Opcional tipada
fn env_parse<T: std::str::FromStr>(nome: &str, padrao: T) -> T {
env::var(nome)
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(padrao)
}
let host = env_opcional("APP_HOST", "127.0.0.1");
let porta: u16 = env_parse("APP_PORT", 3000);
let debug: bool = env_parse("APP_DEBUG", false);
let log_level = env_opcional("LOG_LEVEL", "warn");
let max_conn: u32 = env_parse("MAX_CONEXOES", 10);
println!("Host: {}", host);
println!("Porta: {}", porta);
println!("Debug: {}", debug);
println!("Log Level: {}", log_level);
println!("Max Conexões: {}", max_conn);
// =============================================
// 4. Padrão Config struct
// =============================================
println!("\n=== Config Struct ===");
#[derive(Debug)]
struct Config {
host: String,
porta: u16,
database_url: String,
secret_key: String,
debug: bool,
log_level: String,
max_conexoes: u32,
redis_url: Option<String>,
}
#[derive(Debug)]
enum ConfigErro {
Ausente(String),
Invalido(String, String),
}
impl std::fmt::Display for ConfigErro {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConfigErro::Ausente(nome) => {
write!(f, "Variável '{}' é obrigatória", nome)
}
ConfigErro::Invalido(nome, valor) => {
write!(f, "Valor inválido para '{}': '{}'", nome, valor)
}
}
}
}
impl Config {
fn from_env() -> Result<Config, ConfigErro> {
let database_url = env::var("DATABASE_URL")
.map_err(|_| ConfigErro::Ausente("DATABASE_URL".into()))?;
let secret_key = env::var("SECRET_KEY")
.map_err(|_| ConfigErro::Ausente("SECRET_KEY".into()))?;
let porta_str = env::var("APP_PORT").unwrap_or_else(|_| "8080".into());
let porta: u16 = porta_str.parse()
.map_err(|_| ConfigErro::Invalido("APP_PORT".into(), porta_str))?;
Ok(Config {
host: env::var("APP_HOST").unwrap_or_else(|_| "0.0.0.0".into()),
porta,
database_url,
secret_key,
debug: env::var("APP_DEBUG")
.map(|v| v == "true" || v == "1")
.unwrap_or(false),
log_level: env::var("LOG_LEVEL").unwrap_or_else(|_| "info".into()),
max_conexoes: env::var("MAX_CONEXOES")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(10),
redis_url: env::var("REDIS_URL").ok(),
})
}
fn socket_addr(&self) -> SocketAddr {
format!("{}:{}", self.host, self.porta)
.parse()
.expect("Endereço de socket inválido")
}
}
// Demonstrar o padrão Config
match Config::from_env() {
Ok(config) => {
println!("Config carregada:");
println!(" Endereço: {}", config.socket_addr());
println!(" DB: {}", config.database_url);
println!(" Debug: {}", config.debug);
println!(" Log: {}", config.log_level);
println!(" Redis: {:?}", config.redis_url);
println!(" Max conexões: {}", config.max_conexoes);
}
Err(e) => println!("Erro na config: {}", e),
}
// =============================================
// 5. Listar todas as variáveis de ambiente
// =============================================
println!("\n=== Variáveis APP_* ===");
for (chave, valor) in env::vars() {
if chave.starts_with("APP_") || chave.starts_with("DATABASE") {
println!(" {} = {}", chave, valor);
}
}
// =============================================
// 6. Definir variáveis em tempo de execução
// =============================================
println!("\n=== Definir Variáveis ===");
env::set_var("MINHA_VAR", "hello");
println!("MINHA_VAR = {:?}", env::var("MINHA_VAR"));
env::remove_var("MINHA_VAR");
println!("Após remove: {:?}", env::var("MINHA_VAR"));
// =============================================
// 7. Variáveis de tempo de compilação
// =============================================
println!("\n=== Variáveis de Compilação ===");
println!("Versão do pacote: {}", env!("CARGO_PKG_VERSION"));
println!("Nome do pacote: {}", env!("CARGO_PKG_NAME"));
// option_env! — não causa panic se não existir
match option_env!("BUILD_SHA") {
Some(sha) => println!("Build SHA: {}", sha),
None => println!("Build SHA: não definido"),
}
}
Saída do Programa
=== Leitura Básica ===
HOME: /root
PATH (3 primeiros): ["/usr/local/bin", "/usr/bin", "/bin"]
MINHA_VARIAVEL: não definida
=== dotenvy (.env) ===
Arquivo .env carregado
DATABASE_URL: postgresql://localhost:5432/meudb
APP_PORT: 8080
=== Obrigatórias vs Opcionais ===
Host: 0.0.0.0
Porta: 8080
Debug: true
Log Level: info
Max Conexões: 50
=== Config Struct ===
Config carregada:
Endereço: 0.0.0.0:8080
DB: postgresql://localhost:5432/meudb
Debug: true
Log: info
Redis: Some("redis://localhost:6379")
Max conexões: 50
=== Variáveis APP_* ===
APP_PORT = 8080
APP_DEBUG = true
APP_HOST = 0.0.0.0
DATABASE_URL = postgresql://localhost:5432/meudb
=== Definir Variáveis ===
MINHA_VAR = Ok("hello")
Após remove: Err(NotPresent)
=== Variáveis de Compilação ===
Versão do pacote: 0.1.0
Nome do pacote: receita-env
Build SHA: não definido
Exemplo de Arquivo .env
# Banco de dados
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
# Servidor
APP_HOST=0.0.0.0
APP_PORT=8080
APP_DEBUG=true
# Segurança
SECRET_KEY=minha-chave-secreta-muito-longa
# Logging
LOG_LEVEL=debug
# Serviços externos
REDIS_URL=redis://localhost:6379
Boas Práticas
- Nunca commite o
.envno Git — adicione ao.gitignore. Use.env.examplecomo referência. - Valide variáveis na inicialização — falhe cedo com mensagens claras se algo está faltando.
- Use o padrão Config struct — centralize todas as variáveis em uma struct validada.
dotenvypara desenvolvimento, env vars para produção — em produção, configure via Docker, systemd ou plataforma de deploy.env!()para valores de compilação — use para versão do pacote, nome e hash do build.
Veja Também
- Ler Arquivo TOML em Rust — combine variáveis de ambiente com arquivos de configuração TOML
- Ler Arquivo YAML em Rust — configuração YAML como alternativa
- Tratar Erros em Rust — trate erros de variáveis ausentes de forma robusta
- Tutorial: API REST com Axum — configure APIs com env vars
- Tutorial: CLI com Clap — combine env vars com argumentos de linha de comando
- Variáveis de ambiente em Zig — compare com a abordagem de Zig para configuração