Introdução
Bem-vindo(a) a Aceitando Argumentos da Linha de Comando. Este laboratório faz parte do Livro de Rust. Você pode praticar suas habilidades em Rust no LabEx.
Neste laboratório, criaremos um novo projeto chamado "minigrep" que aceita dois argumentos da linha de comando: uma string para pesquisar e um caminho de arquivo.
Aceitando Argumentos da Linha de Comando
Vamos criar um novo projeto com, como sempre, cargo new. Chamaremos nosso projeto de minigrep para distingui-lo da ferramenta grep que você pode já ter em seu sistema.
$ cargo new minigrep
Created binary (application) `minigrep` project
$ cd minigrep
A primeira tarefa é fazer com que minigrep aceite seus dois argumentos da linha de comando: o caminho do arquivo e uma string para pesquisar. Ou seja, queremos ser capazes de executar nosso programa com cargo run, dois hífens para indicar que os argumentos a seguir são para nosso programa, em vez de para o cargo, uma string para pesquisar e um caminho para um arquivo para pesquisar, assim:
cargo run -- searchstring example-filename.txt
No momento, o programa gerado por cargo new não pode processar os argumentos que lhe damos. Algumas bibliotecas existentes em https://crates.io podem ajudar a escrever um programa que aceite argumentos da linha de comando, mas como você está apenas aprendendo este conceito, vamos implementar essa capacidade nós mesmos.
Lendo os Valores dos Argumentos
Para permitir que minigrep leia os valores dos argumentos da linha de comando que passamos para ele, precisaremos da função std::env::args fornecida na biblioteca padrão do Rust. Esta função retorna um iterador dos argumentos da linha de comando passados para minigrep. Abordaremos iteradores completamente no Capítulo 13. Por enquanto, você só precisa saber dois detalhes sobre iteradores: iteradores produzem uma série de valores, e podemos chamar o método collect em um iterador para transformá-lo em uma coleção, como um vetor, que contém todos os elementos que o iterador produz.
O código na Listagem 12-1 permite que seu programa minigrep leia quaisquer argumentos da linha de comando passados para ele e, em seguida, colete os valores em um vetor.
Nome do arquivo: src/main.rs
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
dbg!(args);
}
Listagem 12-1: Coletando os argumentos da linha de comando em um vetor e imprimindo-os
Primeiro, trazemos o módulo std::env para o escopo com uma instrução use para que possamos usar sua função args. Observe que a função std::env::args está aninhada em dois níveis de módulos. Como discutimos no Capítulo 7, em casos em que a função desejada está aninhada em mais de um módulo, optamos por trazer o módulo pai para o escopo em vez da função. Ao fazer isso, podemos usar facilmente outras funções de std::env. Também é menos ambíguo do que adicionar use std::env::args e, em seguida, chamar a função apenas com args, porque args pode ser facilmente confundido com uma função que é definida no módulo atual.
A Função args e Unicode Inválido
Observe que
std::env::argsentrará em pânico se algum argumento contiver Unicode inválido. Se seu programa precisar aceitar argumentos contendo Unicode inválido, usestd::env::args_osem vez disso. Essa função retorna um iterador que produz valoresOsStringem vez de valoresString. Escolhemos usarstd::env::argsaqui por simplicidade, porque os valoresOsStringdiferem por plataforma e são mais complexos de trabalhar do que os valoresString.
Na primeira linha de main, chamamos env::args e imediatamente usamos collect para transformar o iterador em um vetor contendo todos os valores produzidos pelo iterador. Podemos usar a função collect para criar muitos tipos de coleções, então anotamos explicitamente o tipo de args para especificar que queremos um vetor de strings. Embora você raramente precise anotar tipos em Rust, collect é uma função que você frequentemente precisa anotar porque o Rust não é capaz de inferir o tipo de coleção que você deseja.
Finalmente, imprimimos o vetor usando a macro de depuração. Vamos tentar executar o código primeiro sem argumentos e depois com dois argumentos:
$ cargo run
--snip--
[src/main.rs:5] args = [
"target/debug/minigrep",
]
$ cargo run -- needle haystack
--snip--
[src/main.rs:5] args = [
"target/debug/minigrep",
"needle",
"haystack",
]
Observe que o primeiro valor no vetor é "target/debug/minigrep", que é o nome do nosso binário. Isso corresponde ao comportamento da lista de argumentos em C, permitindo que os programas usem o nome pelo qual foram invocados em sua execução. É frequentemente conveniente ter acesso ao nome do programa caso você queira imprimi-lo em mensagens ou alterar o comportamento do programa com base em qual alias da linha de comando foi usado para invocar o programa. Mas, para os propósitos deste capítulo, vamos ignorá-lo e salvar apenas os dois argumentos de que precisamos.
Salvando os Valores dos Argumentos em Variáveis
O programa atualmente é capaz de acessar os valores especificados como argumentos da linha de comando. Agora precisamos salvar os valores dos dois argumentos em variáveis para que possamos usar os valores em todo o restante do programa. Fazemos isso na Listagem 12-2.
Nome do arquivo: src/main.rs
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
let query = &args[1];
let file_path = &args[2];
println!("Searching for {}", query);
println!("In file {}", file_path);
}
Listagem 12-2: Criando variáveis para armazenar o argumento de consulta e o argumento do caminho do arquivo
Como vimos quando imprimimos o vetor, o nome do programa ocupa o primeiro valor no vetor em args[0], então estamos começando os argumentos no índice 1. O primeiro argumento que minigrep recebe é a string que estamos procurando, então colocamos uma referência ao primeiro argumento na variável query. O segundo argumento será o caminho do arquivo, então colocamos uma referência ao segundo argumento na variável file_path.
Imprimimos temporariamente os valores dessas variáveis para provar que o código está funcionando como pretendemos. Vamos executar este programa novamente com os argumentos test e sample.txt:
$ cargo run -- test sample.txt
Compiling minigrep v0.1.0 (file:///projects/minigrep)
Finished dev [unoptimized + debuginfo] target(s) in 0.0s
Running `target/debug/minigrep test sample.txt`
Searching for test
In file sample.txt
Ótimo, o programa está funcionando! Os valores dos argumentos que precisamos estão sendo salvos nas variáveis corretas. Mais tarde, adicionaremos algum tratamento de erros para lidar com certas situações potencialmente errôneas, como quando o usuário não fornece argumentos; por enquanto, ignoraremos essa situação e trabalharemos na adição de recursos de leitura de arquivos.
Resumo
Parabéns! Você concluiu o laboratório de Aceitação de Argumentos da Linha de Comando. Você pode praticar mais laboratórios no LabEx para aprimorar suas habilidades.