Clean Code em Rust: princípios e exemplos
Escrever código limpo não é apenas seguir tendências da moda, mas uma habilidade fundamental que diferencia um profissional de um amador. No mundo da programação de sistemas, onde Rust ganha cada vez mais popularidade, a capacidade de escrever código compreensível, sustentável e seguro torna-se criticamente importante. Neste artigo, vamos analisar os princípios básicos do Clean Code aplicados ao Rust, apoiando-os com exemplos práticos.
1. Nomenclatura: diga o que a variável faz
A nomenclatura correta é a base de um código legível. Em Rust, onde a tipagem forte e a propriedade (ownership) já fornecem muita informação, bons nomes tornam o código autodocumentável.
Exemplo ruim:
fn calc(x: &[i32]) -> i32 { let mut r = 0; for i in x { r += i; } r}Exemplo bom (Clean Code):
fn calculate_sum(numbers: &[i32]) -> i32 { let mut total = 0; for number in numbers { total += number; } total}Use verbos para funções (calculate, fetch, validate) e substantivos para variáveis (total, user_name, config). Evite abreviações como tmp, res ou val — elas não dizem nada sobre o propósito.
2. Funções: pequenas e com uma única responsabilidade
O princípio da responsabilidade única (Single Responsibility) aplica-se não apenas a classes, mas também a funções. Cada função deve fazer exatamente uma coisa e fazê-la bem.
Exemplo ruim (função faz demais):
fn process_user_data(user: &User) -> Result<(), String> { // Validação if user.name.is_empty() { return Err("Name is empty".to_string()); } // Logging println!("Processing user: {}", user.name); // Salvar no BD save_to_database(user)?; // Enviar email send_welcome_email(user.email)?; Ok(())}Exemplo bom (divisão em funções pequenas):
fn process_user_data(user: &User) -> Result<(), AppError> { validate_user(user)?; log_user_processing(user); save_user_to_db(user)?; send_welcome_email(&user.email)?; Ok(())}
fn validate_user(user: &User) -> Result<(), AppError> { if user.name.is_empty() { return Err(AppError::Validation("Name is required".into())); } Ok(())}
fn log_user_processing(user: &User) { println!("Processing user: {}", user.name);}Observe: cada função agora tem uma responsabilidade clara, e o código ficou mais fácil de testar e modificar.
3. Tratamento de erros: use Result e seus próprios tipos de erro
Em Rust não há exceções, e isso é ótimo. Clean Code em Rust significa trabalhar explicitamente com erros através dos tipos Result<T, E> e Option<T>. Evite pânicos (unwrap(), expect()) em código de produção.
Exemplo ruim (pânico ao erro):
fn read_config(path: &str) -> Config { let content = std::fs::read_to_string(path).unwrap(); serde_json::from_str(&content).unwrap()}Exemplo bom (tratamento limpo de erros):
#[derive(Debug, thiserror::Error)]pub enum ConfigError { #[error("Failed to read file: {0}")] IoError(#[from] std::io::Error), #[error("Failed to parse config: {0}")] ParseError(#[from] serde_json::Error),}
fn read_config(path: &str) -> Result<Config, ConfigError> { let content = std::fs::read_to_string(path)?; let config: Config = serde_json::fr