Skip to content

Projeto descrito na documentação da linguagem Rust utilizando conceitos de: Organização de código usando vetores e strings, Tratamento de erros usando Traits e Lifetimes, Escrita de testes

Notifications You must be signed in to change notification settings

crispim1411/minigrep

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Projeto: Construindo um programa de linha de comando

Projeto descrito na documentação da linguagem Rust com comentários a partir das explicações.

https://doc.rust-lang.org/book/ch12-00-an-io-project.html

Considerações de Modularidade e Tratamento de Erro

  1. Evitar da função main ter mais que uma responsabilidade
  2. agrupar variáveis com um certo propósito em estruturas
  3. Se preocupar se o erro retornado é explicativo
  4. Concentrar o tratamento de erro num único lugar

Separação dos binários

  1. Mover a lógica para lib.rs
  2. Partes pequenas de código podem permanecer em main.rs
  3. Quando se tornar complexo extrair de main.rs para lib.rs

Responsabilidades em main.rs

  • Chamar lógica passando os argumentos
  • Setar alguma configuração
  • Chamar função run em lib.rs
  • Tratar erro caso run retornar um erro

Test-Driven Development (TDD)

  1. Escrever um teste que falhe e receber a falha esperada ao rodar
  2. Modificar para um teste que retorne sucesso
  3. Refatorar o código e verificar que os testes continuem passando
  4. Repetir do passo 1

Ler argumentos

let args: Vec<String> = env::args().collect();

Obs: Chamada env::args ao invés de args evita ambiguidade pois indica o módulo

Lendo um arquivo

let contents = fs::read_to_string(filename)
    .expect("Something went wrong reading the file");

Agrupar variáveis numa estrutura

struct Config {
    query: String,
    filename: String,
}

impl Config {
    fn new(args: &[String]) -> Config {
        if args.len() < 3 {
            panic!("not enough arguments");
        }

        let query = args[1].clone();
        let filename = args[2].clone();

        Config { query, filename }
    }
}

Retornando tipo Result invés de chamar Panic

impl Config {
    fn new(args: &[String]) -> Result<Config, &'static str> {
        if args.len() < 3 {
            return Err("not enough arguments");
        }

        let query = args[1].clone();
        let filename = args[2].clone();

        Ok(Config { query, filename })
    }
}
  • Obs: A chamada em main.rs se torna um unwrap
let config = Config::new(&args).unwrap_or_else(|err| {
    println!("Problem parsing arguments: {}", err);
    process::exit(1);
});

Adaptando o erro na leitura de arquivo

  • lib.rs
fn run(config: Config) -> Result<(), Box<dyn Error>> {
    let contents = fs::read_to_string(config.filename)?;

    println!("With text:\n{}", contents);

    Ok(())
}
  • main.rs
if let Err(e) = run(config) {
    println!("Application error: {}", e);

    process::exit(1);
}

Referenciando variável por lifetime

pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    vec![]
}
  • Desta forma os dados do resultado serão válidos enquanto os dados em contents forem válidos.
  • Evitando também do compilador assumir associação com query invés de contents.

Lendo variáveis de ambiente

let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
  • Obs: foi usado is_err para verificação, retornando true se não estiver setada.
$export CASE_INSENSITIVE=1

Alterando a escrita dos erros

  • Usando eprintln! para evitar do erro aparecer no resultado do código
eprintln!("Problem parsing arguments: {}", err);

About

Projeto descrito na documentação da linguagem Rust utilizando conceitos de: Organização de código usando vetores e strings, Tratamento de erros usando Traits e Lifetimes, Escrita de testes

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages