Módulo std::env em Rust
O módulo std::env fornece funções para interagir com o ambiente de execução do processo: variáveis de ambiente, argumentos da linha de comando, diretório de trabalho atual, caminho do executável, diretório temporário e constantes sobre a plataforma (sistema operacional, arquitetura). É essencial para programas CLI, configuração por ambiente e código cross-platform.
Visão geral e tipos-chave
Variáveis de ambiente
env::var(key) — lê uma variável de ambiente como Result<String, VarError>env::var_os(key) — lê como Option<OsString> (não falha em Unicode inválido)env::set_var(key, value) — define uma variável de ambienteenv::remove_var(key) — remove uma variável de ambienteenv::vars() — iterador sobre todas as variáveis como (String, String)env::vars_os() — iterador sobre todas como (OsString, OsString)
Argumentos e caminhos
env::args() — iterador sobre argumentos CLI como Stringenv::args_os() — iterador sobre argumentos como OsStringenv::current_dir() — diretório de trabalho atualenv::set_current_dir(path) — altera o diretório de trabalhoenv::current_exe() — caminho do executável atualenv::temp_dir() — diretório temporário do sistema
consts::OS — sistema operacional ("linux", "windows", "macos")consts::ARCH — arquitetura ("x86_64", "aarch64", "arm")consts::FAMILY — família do SO ("unix", "windows")consts::EXE_SUFFIX — sufixo de executáveis ("" no Unix, ".exe" no Windows)consts::DLL_SUFFIX — sufixo de bibliotecas dinâmicas (.so, .dll, .dylib)
use std::env;
fn configuracao() -> (String, u16, String) {
let host = env::var("APP_HOST").unwrap_or_else(|_| "127.0.0.1".to_string());
let porta: u16 = env::var("APP_PORT")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(8080);
let modo = env::var("APP_MODE").unwrap_or_else(|_| "development".to_string());
(host, porta, modo)
}
fn main() {
let (host, porta, modo) = configuracao();
println!("Servidor: {}:{} (modo: {})", host, porta, modo);
}
Processar argumentos da linha de comando
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
// args[0] é o nome do programa
println!("Programa: {}", args[0]);
println!("Argumentos: {:?}", &args[1..]);
if args.len() < 2 {
eprintln!("Uso: {} <comando> [opcoes]", args[0]);
eprintln!("Comandos: listar, criar, remover");
std::process::exit(1);
}
match args[1].as_str() {
"listar" => println!("Listando itens..."),
"criar" => {
let nome = args.get(2).map(|s| s.as_str()).unwrap_or("sem_nome");
println!("Criando: {}", nome);
}
"remover" => {
let nome = args.get(2).map(|s| s.as_str()).unwrap_or("");
if nome.is_empty() {
eprintln!("Erro: especifique o nome para remover");
std::process::exit(1);
}
println!("Removendo: {}", nome);
}
cmd => {
eprintln!("Comando desconhecido: '{}'", cmd);
std::process::exit(1);
}
}
}
use std::env;
fn info_plataforma() {
println!("=== Informações da Plataforma ===");
println!("SO: {}", env::consts::OS);
println!("Arquitetura: {}", env::consts::ARCH);
println!("Família: {}", env::consts::FAMILY);
println!("Sufixo EXE: '{}'", env::consts::EXE_SUFFIX);
println!("Sufixo DLL: '{}'", env::consts::DLL_SUFFIX);
println!("Prefixo DLL: '{}'", env::consts::DLL_PREFIX);
println!("\n=== Caminhos ===");
match env::current_dir() {
Ok(dir) => println!("Dir atual: {}", dir.display()),
Err(e) => println!("Dir atual: erro: {}", e),
}
match env::current_exe() {
Ok(exe) => println!("Executável: {}", exe.display()),
Err(e) => println!("Executável: erro: {}", e),
}
println!("Dir temp: {}", env::temp_dir().display());
}
fn main() {
info_plataforma();
}
Tabela de funções e constantes
Funções de variáveis de ambiente
| Função | Retorno | Descrição |
|---|
env::var(key) | Result<String, VarError> | Lê variável como String UTF-8 |
env::var_os(key) | Option<OsString> | Lê variável como OsString |
env::set_var(key, value) | () | Define variável de ambiente |
env::remove_var(key) | () | Remove variável de ambiente |
env::vars() | Vars | Iterador (String, String) |
env::vars_os() | VarsOs | Iterador (OsString, OsString) |
Funções de argumentos e caminhos
| Função | Retorno | Descrição |
|---|
env::args() | Args | Iterador sobre argumentos CLI (String) |
env::args_os() | ArgsOs | Iterador sobre argumentos CLI (OsString) |
env::current_dir() | io::Result<PathBuf> | Diretório de trabalho atual |
env::set_current_dir(path) | io::Result<()> | Altera diretório de trabalho |
env::current_exe() | io::Result<PathBuf> | Caminho do executável |
env::temp_dir() | PathBuf | Diretório temporário do sistema |
Constantes (env::consts)
| Constante | Tipo | Exemplos |
|---|
consts::OS | &str | "linux", "windows", "macos" |
consts::ARCH | &str | "x86_64", "aarch64", "arm" |
consts::FAMILY | &str | "unix", "windows" |
consts::EXE_SUFFIX | &str | "" (Unix), ".exe" (Windows) |
consts::EXE_EXTENSION | &str | "" (Unix), "exe" (Windows) |
consts::DLL_PREFIX | &str | "lib" (Unix), "" (Windows) |
consts::DLL_SUFFIX | &str | ".so" (Linux), ".dll" (Win), ".dylib" (macOS) |
consts::DLL_EXTENSION | &str | "so", "dll", "dylib" |
Exemplos práticos
use std::env;
use std::net::SocketAddr;
#[derive(Debug)]
struct Config {
endereco: SocketAddr,
database_url: String,
nivel_log: String,
max_conexoes: usize,
modo_debug: bool,
}
impl Config {
fn do_ambiente() -> Result<Self, String> {
let host = env::var("HOST").unwrap_or_else(|_| "0.0.0.0".to_string());
let porta = env::var("PORT")
.unwrap_or_else(|_| "3000".to_string())
.parse::<u16>()
.map_err(|e| format!("PORT inválida: {}", e))?;
let endereco: SocketAddr = format!("{}:{}", host, porta)
.parse()
.map_err(|e| format!("Endereço inválido: {}", e))?;
let database_url = env::var("DATABASE_URL")
.map_err(|_| "DATABASE_URL é obrigatória".to_string())?;
let nivel_log = env::var("LOG_LEVEL").unwrap_or_else(|_| "info".to_string());
let validos = ["trace", "debug", "info", "warn", "error"];
if !validos.contains(&nivel_log.as_str()) {
return Err(format!(
"LOG_LEVEL inválido: '{}'. Use: {:?}",
nivel_log, validos
));
}
let max_conexoes = env::var("MAX_CONN")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(100);
let modo_debug = env::var("DEBUG")
.map(|v| v == "1" || v.to_lowercase() == "true")
.unwrap_or(false);
Ok(Config {
endereco,
database_url,
nivel_log,
max_conexoes,
modo_debug,
})
}
}
fn main() {
match Config::do_ambiente() {
Ok(config) => {
println!("Configuração carregada:");
println!(" Endereço: {}", config.endereco);
println!(" Database: {}", config.database_url);
println!(" Log: {}", config.nivel_log);
println!(" Max conexões: {}", config.max_conexoes);
println!(" Debug: {}", config.modo_debug);
}
Err(e) => {
eprintln!("Erro na configuração: {}", e);
eprintln!("\nVariáveis esperadas:");
eprintln!(" DATABASE_URL (obrigatória)");
eprintln!(" HOST (padrão: 0.0.0.0)");
eprintln!(" PORT (padrão: 3000)");
eprintln!(" LOG_LEVEL (padrão: info)");
eprintln!(" MAX_CONN (padrão: 100)");
eprintln!(" DEBUG (padrão: false)");
std::process::exit(1);
}
}
}
Exemplo 2: Parser de argumentos CLI manual
use std::env;
use std::collections::HashMap;
#[derive(Debug)]
struct ArgsParser {
programa: String,
comando: Option<String>,
flags: HashMap<String, Option<String>>,
posicionais: Vec<String>,
}
impl ArgsParser {
fn parse() -> Self {
let mut args = env::args();
let programa = args.next().unwrap_or_default();
let mut comando = None;
let mut flags = HashMap::new();
let mut posicionais = Vec::new();
let args_restantes: Vec<String> = args.collect();
let mut i = 0;
while i < args_restantes.len() {
let arg = &args_restantes[i];
if arg.starts_with("--") {
let chave = arg.trim_start_matches("--").to_string();
if let Some((k, v)) = chave.split_once('=') {
flags.insert(k.to_string(), Some(v.to_string()));
} else if i + 1 < args_restantes.len() && !args_restantes[i + 1].starts_with('-') {
flags.insert(chave, Some(args_restantes[i + 1].clone()));
i += 1;
} else {
flags.insert(chave, None);
}
} else if arg.starts_with('-') {
let chave = arg.trim_start_matches('-').to_string();
flags.insert(chave, None);
} else if comando.is_none() {
comando = Some(arg.clone());
} else {
posicionais.push(arg.clone());
}
i += 1;
}
ArgsParser {
programa,
comando,
flags,
posicionais,
}
}
fn tem_flag(&self, nome: &str) -> bool {
self.flags.contains_key(nome)
}
fn valor_flag(&self, nome: &str) -> Option<&str> {
self.flags.get(nome).and_then(|v| v.as_deref())
}
}
fn main() {
let args = ArgsParser::parse();
println!("Programa: {}", args.programa);
println!("Comando: {:?}", args.comando);
println!("Flags: {:?}", args.flags);
println!("Posicionais: {:?}", args.posicionais);
// Uso: meu_app build --release --target x86_64 arquivo.rs
if args.tem_flag("help") || args.tem_flag("h") {
println!("\nUso: {} <comando> [opcoes] [arquivos...]", args.programa);
println!("Comandos: build, test, run");
println!("Opções: --release, --target <alvo>, --verbose, -h/--help");
}
if let Some(alvo) = args.valor_flag("target") {
println!("Compilando para: {}", alvo);
}
}
Exemplo 3: Listar e filtrar variáveis de ambiente
use std::env;
fn listar_variaveis(filtro: Option<&str>) {
let mut vars: Vec<(String, String)> = env::vars().collect();
vars.sort_by(|a, b| a.0.cmp(&b.0));
let filtradas: Vec<&(String, String)> = match filtro {
Some(f) => vars.iter().filter(|(k, _)| {
k.to_lowercase().contains(&f.to_lowercase())
}).collect(),
None => vars.iter().collect(),
};
println!("Variáveis de ambiente ({} encontradas):", filtradas.len());
for (chave, valor) in &filtradas {
let valor_exib = if valor.len() > 80 {
format!("{}...", &valor[..77])
} else {
valor.clone()
};
println!(" {}={}", chave, valor_exib);
}
}
fn main() {
let args: Vec<String> = env::args().collect();
let filtro = args.get(1).map(|s| s.as_str());
listar_variaveis(filtro);
}
use std::env;
use std::path::PathBuf;
fn diretorio_dados() -> PathBuf {
match env::consts::OS {
"linux" => {
env::var("XDG_DATA_HOME")
.map(PathBuf::from)
.unwrap_or_else(|_| {
let home = env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
PathBuf::from(home).join(".local/share")
})
.join("meu_app")
}
"macos" => {
let home = env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
PathBuf::from(home)
.join("Library/Application Support/MeuApp")
}
"windows" => {
env::var("LOCALAPPDATA")
.map(PathBuf::from)
.unwrap_or_else(|_| PathBuf::from("C:\\ProgramData"))
.join("MeuApp")
}
_ => env::temp_dir().join("meu_app"),
}
}
fn nome_executavel(nome_base: &str) -> String {
format!("{}{}", nome_base, env::consts::EXE_SUFFIX)
}
fn nome_biblioteca(nome_base: &str) -> String {
format!(
"{}{}{}",
env::consts::DLL_PREFIX,
nome_base,
env::consts::DLL_SUFFIX
)
}
fn main() {
println!("Diretório de dados: {}", diretorio_dados().display());
println!("Executável: {}", nome_executavel("meu_app"));
println!("Biblioteca: {}", nome_biblioteca("utils"));
// Condicionais em tempo de compilação vs. runtime
// cfg!() é em tempo de compilação (preferível quando possível)
if cfg!(target_os = "linux") {
println!("Compilado para Linux");
}
// env::consts é em runtime (útil para lógica dinâmica)
match env::consts::ARCH {
"x86_64" => println!("Rodando em 64-bit x86"),
"aarch64" => println!("Rodando em ARM 64-bit"),
arch => println!("Rodando em: {}", arch),
}
}
Exemplo 5: Gerenciar arquivos temporários
use std::env;
use std::fs;
use std::io::{self, Write};
use std::path::PathBuf;
struct ArquivoTemporario {
caminho: PathBuf,
}
impl ArquivoTemporario {
fn novo(prefixo: &str, extensao: &str) -> io::Result<Self> {
let dir = env::temp_dir();
let id: u64 = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
let nome = format!("{}_{}.{}", prefixo, id, extensao);
let caminho = dir.join(nome);
// Criar o arquivo vazio
fs::File::create(&caminho)?;
Ok(ArquivoTemporario { caminho })
}
fn caminho(&self) -> &std::path::Path {
&self.caminho
}
fn escrever(&self, conteudo: &str) -> io::Result<()> {
fs::write(&self.caminho, conteudo)
}
fn ler(&self) -> io::Result<String> {
fs::read_to_string(&self.caminho)
}
}
impl Drop for ArquivoTemporario {
fn drop(&mut self) {
if self.caminho.exists() {
let _ = fs::remove_file(&self.caminho);
}
}
}
fn main() -> io::Result<()> {
println!("Diretório temporário: {}", env::temp_dir().display());
let temp = ArquivoTemporario::novo("relatorio", "txt")?;
println!("Arquivo temp: {}", temp.caminho().display());
temp.escrever("Dados temporários do processamento\nLinha 2\n")?;
let conteudo = temp.ler()?;
println!("Conteúdo: {}", conteudo);
// Arquivo é removido automaticamente quando temp sai de escopo
println!("Arquivo será limpo automaticamente");
Ok(())
}
Padrões de tratamento de erro para I/O
VarError: variável não definida vs. Unicode inválido
use std::env;
fn ler_var_obrigatoria(nome: &str) -> Result<String, String> {
match env::var(nome) {
Ok(valor) => {
if valor.trim().is_empty() {
Err(format!("'{}' está definida mas vazia", nome))
} else {
Ok(valor)
}
}
Err(env::VarError::NotPresent) => {
Err(format!("'{}' não está definida", nome))
}
Err(env::VarError::NotUnicode(valor)) => {
// Tentar usar a versão lossy
eprintln!(
"Aviso: '{}' contém caracteres não-Unicode: {:?}",
nome, valor
);
Ok(valor.to_string_lossy().to_string())
}
}
}
fn main() {
let variaveis = ["DATABASE_URL", "API_KEY", "HOME", "INEXISTENTE"];
for var in &variaveis {
match ler_var_obrigatoria(var) {
Ok(v) => println!("{} = {}", var, v),
Err(e) => eprintln!("{}: {}", var, e),
}
}
}
use std::env;
fn main() {
// AVISO: set_var NÃO é thread-safe no Rust!
// Use apenas na inicialização, antes de criar threads.
// Definir variáveis na inicialização
if env::var("RUST_LOG").is_err() {
// Seguro: estamos na thread principal, antes de qualquer spawn
unsafe {
env::set_var("RUST_LOG", "info");
}
}
// Para configuração em tempo de execução, use tipos thread-safe
// como Arc<RwLock<HashMap<String, String>>> em vez de env vars.
}
Dicas de desempenho
- Cache variáveis de ambiente que serão lidas frequentemente. Cada chamada a
env::var() faz uma syscall. - Use
env::args_os() se precisar processar argumentos que podem conter caracteres não-UTF-8 (ex: nomes de arquivo em sistemas com locales exóticos). - Prefira
cfg!() a env::consts::OS para decisões que podem ser resolvidas em tempo de compilação — o compilador otimiza o branch morto. - Colete
args() em Vec apenas uma vez se precisar acessar os argumentos múltiplas vezes.
Veja também