Clean Code em Rust: princípios e exemplos na prática
No mundo do desenvolvimento de software, escrever código funcional é apenas metade do trabalho. A verdadeira maestria se manifesta na capacidade de criar código que seja fácil de ler, manter e expandir. Para a linguagem Rust, conhecida por sua segurança de memória e desempenho, os princípios do Clean Code adquirem uma importância especial. Neste artigo, vamos analisar os princípios-chave para escrever código limpo em Rust e fundamentá-los com exemplos práticos.
Por que o Clean Code é importante para Rust?
Rust é uma linguagem poderosa com um sistema único de ownership e borrowing. Apesar de sua rigidez, é fácil criar código confuso que será difícil de manter. O Clean Code ajuda a:
- Reduzir o número de erros em tempo de compilação e execução.
- Simplificar o trabalho em equipe e as revisões de código.
- Aumentar a produtividade do desenvolvimento através da legibilidade.
- Tornar o código autodocumentável.
Princípio 1: Nomenclatura de variáveis e funções
Um bom nome é a base do código limpo. Em Rust, onde os tipos são estritos, isso é especialmente importante. Evite variáveis de uma letra (exceto para índices de loops) e use snake_case para variáveis e funções.
Exemplo ruim:
fn c(a: &[i32]) -> i32 { let mut s = 0; for i in a { s += i; } s}Exemplo bom (Clean Code):
fn calculate_sum(numbers: &[i32]) -> i32 { let mut sum = 0; for number in numbers { sum += number; } sum}Recomendações:
- Use verbos para funções:
process_data(),validate_input(). - Use substantivos para variáveis:
user_count,error_message. - Evite abreviações:
calc→calculate,tmp→temporary_value.
Princípio 2: Funções pequenas e responsabilidade única
Cada função deve fazer apenas uma coisa. Em Rust, isso ajuda especialmente ao trabalhar com closures e iterators.
Exemplo ruim (função faz demais):
fn process_user_data(users: &[User]) -> Vec<String> { let mut result = Vec::new(); for user in users { if user.age > 18 { let name = user.name.to_uppercase(); result.push(format!("Adult: {}", name)); } else { result.push(format!("Minor: {}", user.name)); } } result}Exemplo bom (divisão em funções pequenas):
fn classify_user(user: &User) -> &str { if user.age > 18 { "Adult" } else { "Minor" }}
fn format_user_name(user: &User) -> String { let classification = classify_user(user); format!("{}: {}", classification, user.name)}
fn process_user_data(users: &[User]) -> Vec<String> { users.iter().map(format_user_name).collect()}Observe: no segundo exemplo, usamos iterators, o que torna o código mais idiomático para Rust.
Princípio 3: Tratamento de erros e Option/Result
Rust oferece os tipos poderosos Option e Result para tratamento de erros. O código limpo exige o tratamento explícito de todos os casos possíveis.
Exemplo ruim (ignorando erros):
fn divide(a: f64, b: f64) -> f64 { if b == 0.0 { panic!("Division by zero!"); } a / b}Exemplo bom (tratamento limpo de erros):
fn divide(a: f64, b: f64) -> Result<f64, String> { if b == 0.0 { Err("Division by zero