Argumentos de linha de comando em Rust
A maioria dos projetinhos que valem a pena para praticar, como uma lista de tarefas ou um contador de palavras, precisa receber dados pela linha de comando em vez de deixar valores fixos no código. Esta página mostra o básico.
Lendo argumentos com std::env::args
Seção intitulada “Lendo argumentos com std::env::args”use std::env;
fn main() { let args: Vec<String> = env::args().collect(); println!("{:?}", args);}Se você compilasse isso em um programa chamado greet e rodasse:
./greet AliceVocê veria:
["greet", "Alice"]O primeiro elemento é sempre o nome do próprio programa. Os argumentos que você digitou de fato começam no índice 1.
Lendo um argumento específico
Seção intitulada “Lendo um argumento específico”use std::env;
fn main() { let args: Vec<String> = env::args().collect();
if args.len() < 2 { println!("Uso: greet <nome>"); return; }
let name = &args[1]; println!("Olá, {name}!");}Rodando com cargo run -- Alice, você teria:
Olá, Alice!Se rodar sem nenhum argumento, você vê a mensagem de uso em vez de o programa quebrar. Verificar args.len() antes de acessar um índice evita um panic quando alguém esquece de passar o argumento.
Convertendo um argumento em número
Seção intitulada “Convertendo um argumento em número”Como todo argumento chega como String, muitas vezes você vai precisar do .parse() (da página de Conversão de tipos) para transformá-lo em número:
use std::env;
fn main() { let args: Vec<String> = env::args().collect();
if args.len() < 2 { println!("Uso: square <número>"); return; }
match args[1].parse::<i32>() { Ok(n) => println!("{}", n * n), Err(_) => println!("Isso não é um número válido"), }}Tratando uma flag simples na mão
Seção intitulada “Tratando uma flag simples na mão”Uma flag é um argumento que liga alguma coisa, como --shout (gritar). Antes de partir para um crate, vale a pena ver como você mesmo trataria uma. O truque que deixa isso fácil de testar é colocar a lógica em uma função que recebe os argumentos como uma slice, &[String]. Assim o main pode passar os argumentos de verdade, e você pode passar uma lista feita à mão enquanto experimenta:
fn run(args: &[String]) { let mut shout = false; let mut names = Vec::new();
// pula o índice 0, o nome do programa for arg in &args[1..] { if arg == "--shout" { shout = true; } else { names.push(arg); } }
if names.is_empty() { println!("Uso: greet [--shout] <nome>..."); return; }
for name in names { let line = format!("Olá, {name}!"); if shout { println!("{}", line.to_uppercase()); } else { println!("{line}"); } }}
fn main() { // Em um programa de verdade, você escreveria: // let args: Vec<String> = std::env::args().collect(); // Aqui montamos a lista na mão, para funcionar também no Playground. let args = vec![ String::from("greet"), String::from("--shout"), String::from("Ana"), String::from("Ben"), ]; run(&args);}Saída:
OLÁ, ANA!OLÁ, BEN!Algumas coisas para reparar:
&args[1..]são todos os argumentos menos o nome do programa. A flag pode aparecer em qualquer posição:greet Ana --shout Bentambém funciona.- Tire o
--shoutda lista e rode de novo: você recebe as saudações normais. Tire os nomes também, e aparece a mensagem de uso. - Como
runnão se importa com a origem da lista, trocar parastd::env::args().collect()é uma mudança de uma linha nomain.
Isso funciona bem para uma ou duas flags. Quando você também quiser versões curtas (-s), valores depois de uma flag (--count 3) e uma mensagem de --help, o laço escrito à mão cresce rápido. Essa é a hora de mudar para o clap.
Para mais do que alguns argumentos: use o clap
Seção intitulada “Para mais do que alguns argumentos: use o clap”Acessar args por índice na mão fica complicado rapidinho quando você precisa de flags (--verbose), valores opcionais ou uma saída de --help que ajude de verdade. A solução padrão é o crate clap, que você adiciona com:
cargo add clap --features deriveuse clap::Parser;
#[derive(Parser)]struct Args { name: String,
#[arg(short, long, default_value_t = 1)] count: u32,}
fn main() { let args = Args::parse(); for _ in 0..args.count { println!("Olá, {}!", args.name); }}Com isso você ganha a leitura dos argumentos, a validação e uma mensagem --help funcionando, tudo gerado a partir da definição da struct. Para scripts rápidos você não precisa do clap, mas para qualquer ferramenta que você vá usar mais de uma vez, ele poupa muito trabalho manual.
Prática
Seção intitulada “Prática”O Playground não consegue passar argumentos de verdade, então os dois exercícios começam com um Vec<String> feito à mão, igualzinho ao que env::args().collect() devolveria. No seu computador, troque pela versão real e experimente cargo run -- ....
1. Liste os argumentos
Seção intitulada “1. Liste os argumentos”Dado vec!["notes", "add", "comprar leite"] (como Strings), imprima quantos argumentos o usuário digitou (sem contar o nome do programa) e depois imprima cada um com a sua posição, começando em 1.
Dica: a contagem é args.len() - 1. Um laço for i in 1..args.len() dá cada índice depois do nome do programa.
Ver solução
fn main() { // finja que isto veio de std::env::args().collect() let args = vec![ String::from("notes"), String::from("add"), String::from("comprar leite"), ];
println!("Você passou {} argumentos", args.len() - 1); for i in 1..args.len() { println!("{i}: {}", args[i]); }}Saída:
Você passou 2 argumentos1: add2: comprar leite"comprar leite" conta como um argumento: em uma linha de comando de verdade, você digitaria entre aspas (cargo run -- add "comprar leite"), e o shell passa tudo como uma única string. O índice 0 continua sendo o nome do programa, e é por isso que o laço começa em 1.
2. Some os números
Seção intitulada “2. Some os números”Dado vec!["sum", "4", "10", "x", "6"], some todos os argumentos que são números válidos e imprima um aviso para os que não são. No fim, imprima o total.
Dica: percorra &args[1..] e use match arg.parse::<i32>(), como em Convertendo um argumento em número. Deixe um let mut total = 0; fora do laço.
Ver solução
fn main() { let args = vec![ String::from("sum"), String::from("4"), String::from("10"), String::from("x"), String::from("6"), ];
let mut total = 0; for arg in &args[1..] { match arg.parse::<i32>() { Ok(n) => total += n, Err(_) => println!("Ignorando '{arg}': não é um número"), } } println!("Total: {total}");}Saída:
Ignorando 'x': não é um númeroTotal: 20Todo argumento chega como texto, até o "4", então cada um precisa ser convertido. Pular uma entrada ruim com uma mensagem é mais amigável do que fazer o programa quebrar. Você também poderia parar no primeiro argumento ruim com return, se isso combinar melhor com o seu programa.
std::env::args().collect()devolve umVec<String>com os argumentos da linha de comando. O índice 0 é sempre o nome do programa.- Verifique
args.len()antes de acessar um índice, para que a falta de um argumento não cause um panic. - O
.parse()transforma um argumentoStringem número, igual em qualquer outro lugar. - Coloque o tratamento dos argumentos em uma função que recebe
&[String], para poder testá-la com uma lista feita à mão. - Para qualquer coisa além de alguns argumentos simples, o crate
clapé a escolha padrão e bem testada.
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”Teste o que aprendeu
1. Quando você chama env::args().collect(), qual é o primeiro elemento do Vec resultante?
2. Por que verificar args.len() antes de acessar args por índice?
3. O que o crate clap oferece que acessar args na mão não oferece?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- Accepting Command Line Arguments — The Rust Book (em inglês): o começo do projeto
minigrepdo livro, que lê uma palavra de busca e um nome de arquivo da linha de comando. std::env::args— documentação da std (em inglês): o que exatamenteargs()devolve, e quando ele causa um panic (argumentos que não são Unicode válido).- Program arguments — Rust by Example (em inglês): um exemplo curto, e uma continuação sobre como fazer match nos argumentos com
match. - Command Line Applications in Rust (em inglês): um minilivro gratuito sobre como criar ferramentas de linha de comando de verdade, da leitura dos argumentos aos testes e à distribuição.
- Tutorial de derive do clap — docs.rs (em inglês): um guia passo a passo de flags, opções, subcomandos e validação com o
clap.