Módulo std::env em Rust

Referência completa do módulo std::env em Rust: var, set_var, args, current_dir, current_exe, temp_dir e constantes OS e ARCH com exemplos.

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 ambiente
  • env::remove_var(key) — remove uma variável de ambiente
  • env::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 String
  • env::args_os() — iterador sobre argumentos como OsString
  • env::current_dir() — diretório de trabalho atual
  • env::set_current_dir(path) — altera o diretório de trabalho
  • env::current_exe() — caminho do executável atual
  • env::temp_dir() — diretório temporário do sistema

Constantes da plataforma (env::consts)

  • 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)

Padrões comuns com código

Ler variáveis de ambiente com fallback

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);
        }
    }
}

Informações da plataforma

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çãoRetornoDescriçã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()VarsIterador (String, String)
env::vars_os()VarsOsIterador (OsString, OsString)

Funções de argumentos e caminhos

FunçãoRetornoDescrição
env::args()ArgsIterador sobre argumentos CLI (String)
env::args_os()ArgsOsIterador 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()PathBufDiretório temporário do sistema

Constantes (env::consts)

ConstanteTipoExemplos
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

Exemplo 1: Configuração por ambiente com validação

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);
}

Exemplo 4: Código cross-platform com consts

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),
        }
    }
}

Cuidado com set_var e segurança entre threads

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