Clean Code en Rust: principios y ejemplos prácticos
En el mundo del desarrollo de software, escribir código que funcione es solo la mitad del trabajo. La verdadera maestría se demuestra en la capacidad de crear código que sea fácil de leer, mantener y ampliar. Para el lenguaje Rust, conocido por su seguridad de memoria y rendimiento, los principios de Clean Code adquieren una importancia especial. En este artículo, analizaremos los principios clave para escribir código limpio en Rust y los respaldaremos con ejemplos prácticos.
¿Por qué es importante Clean Code para Rust?
Rust es un lenguaje potente con un sistema único de propiedad (ownership) y préstamo (borrowing). A pesar de su rigor, es fácil crear código confuso que sea difícil de mantener. Clean Code ayuda a:
- Reducir la cantidad de errores en tiempo de compilación y ejecución.
- Simplificar el trabajo en equipo y las revisiones de código.
- Aumentar la productividad del desarrollo gracias a la legibilidad.
- Hacer que el código sea autodocumentado.
Principio 1: Nombres de variables y funciones
Un buen nombre es la base del código limpio. En Rust, donde los tipos son estrictos, esto es especialmente importante. Evite las variables de una sola letra (excepto para índices de bucles) y use snake_case para variables y funciones.
Ejemplo malo:
fn c(a: &[i32]) -> i32 { let mut s = 0; for i in a { s += i; } s}Ejemplo bueno (Clean Code):
fn calculate_sum(numbers: &[i32]) -> i32 { let mut sum = 0; for number in numbers { sum += number; } sum}Recomendaciones:
- Use verbos para funciones:
process_data(),validate_input(). - Use sustantivos para variables:
user_count,error_message. - Evite abreviaturas:
calc→calculate,tmp→temporary_value.
Principio 2: Funciones pequeñas y responsabilidad única
Cada función debe hacer solo una cosa. En Rust, esto ayuda especialmente al trabajar con closures e iteradores.
Ejemplo malo (la función hace demasiado):
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}Ejemplo bueno (división en funciones pequeñas):
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: en el segundo ejemplo usamos iteradores, lo que hace que el código sea más idiomático para Rust.
Principio 3: Manejo de errores y Option/Result
Rust ofrece los potentes tipos Option y Result para el manejo de errores. El código limpio requiere un manejo explícito de todos los casos posibles.
Ejemplo malo (ignorar errores):
fn divide(a: f64, b: f64) -> f64 { if b == 0.0 { panic!("Division by zero!"); } a / b}Ejemplo bueno (manejo limpio de errores):
fn divide(a: f64, b: f64) -> Result<f64, String> { if b == 0.0 { Err("Division by zero