Código Limpo em Rust: princípios, expressões idiomáticas e exemplos práticos

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: Por que Código Limpo em Rust Não É Apenas Estilo



Rust é uma linguagem que combina o desempenho do C++ com segurança de memória, mas seu sistema único de ownership e borrowing exige uma abordagem especial para escrever código. Os princípios de Código Limpo formulados por Robert Martin ganham uma nova dimensão em Rust: aqui, a limpeza do código está diretamente ligada à correção, desempenho e facilidade de manutenção.



Neste artigo, exploraremos como aplicar expressões idiomáticas do Rust para criar código autodocumentado, seguro e eficiente. Você verá como substituir erros típicos de iniciantes por soluções elegantes aceitas pela comunidade Rust.



1. Tratamento de Erros Idiomático: De unwrap() a Padrões Expressivos



Um dos primeiros problemas que iniciantes enfrentam é o uso excessivo de unwrap(). Em Código Limpo, um erro não é uma exceção, mas parte da arquitetura. O Rust fornece tipos poderosos Option e Result para tratamento explícito de casos extremos.



Exemplo Ruim (Código Sujo)

fn parse_number(input: &str) -> i32 {    input.parse::<i32>().unwrap()}

fn main() { let val = parse_number("not a number"); println!("{}", val); // Pânico!}


Exemplo Bom (Código Limpo)

use std::num::ParseIntError;

fn parse_number(input: &str) -> Result<i32, ParseIntError> { input.parse::<i32>()}

fn main() { match parse_number("42") { Ok(n) => println!("Número: {}", n), Err(e) => eprintln!("Erro: {}", e), }}


Por que isso é Código Limpo? A função declara explicitamente que pode falhar. O código chamador decide como lidar com a falha — registrar no log, tentar novamente ou propagar o erro para cima.



Técnica Avançada: O Operador ?

fn read_config(path: &str) -> Result<String, std::io::Error> {    let content = std::fs::read_to_string(path)?;    Ok(content)}


O operador ? é uma substituição elegante para expressões match prolixas. Ele retorna o erro para cima se o resultado for Err, e extrai o valor se for Ok. Isso reduz o aninhamento e melhora a legibilidade.



2. Ownership e Borrowing: Projete a Arquitetura, Não Lute Contra o Compilador



O sistema de ownership do Rust pode parecer uma limitação, mas na verdade ajuda a escrever código limpo. Os problemas começam quando um desenvolvedor tenta "enganar" o compilador com clone() ou Rc<RefCell> sem necessidade.



Antipadrão: Clonagem Excessiva

fn process_data(data: Vec<String>) {    for item in data.clone() { // Por que clonar?        println!("{}", item);    }    // data não é mais usada}


Solução Limpa: Borrowing por Referência

fn process_data(data: &[String]) {    for item in data {        println!("{}", item);    }}

fn main() { let my_data = vec!["a".to_string(), "b".to_string()]; process_data(&my_data); // A ownership permanece no main println!("Vetor ainda está acessível: {:?}", my_data);}


Princípio de Código Limpo: Escopo mínimo para mutações. Se uma função não deve modificar dados — aceite uma referência imutável &T. Se for necessária modificação — &mut T. Isso torna o código previsível e seguro.



Expressão Idiomática: Passagem de Ownership para Transformação

fn to_uppercase(mut text: String) -> String {    text.make_ascii_uppercase();    text // A ownership é retornada}


Aqui, a função "consome" a string, modifica-a e a retorna. Isso é eficiente (sem cópia) e explícito: quem chama entende que a variável original não é mais válida.



3. Closures e Programação Funcional

Blogs

Book Recommendations