Clean Code en Rust: principios y ejemplos para principiantes
Rust es un potente lenguaje de programación de sistemas, conocido por su rendimiento y seguridad de memoria. Sin embargo, como en cualquier otro lenguaje, escribir código limpio (Clean Code) es una habilidad críticamente importante. El código limpio es más fácil de leer, mantener y ampliar. Para los desarrolladores principiantes en Rust, dominar los principios de Clean Code desde el principio ayudará a evitar errores típicos y hará que su código sea elegante y confiable. En este artículo, analizaremos los principios fundamentales del código limpio en Rust con ejemplos concretos.
1. Nomenclatura de variables y funciones
Un buen nombre debe explicar la intención del programador. En Rust se acostumbra usar snake_case para variables, funciones y módulos, y CamelCase para tipos y traits.
Ejemplo incorrecto:
fn c(a: i32, b: i32) -> i32 { let x = a + b; x}Ejemplo correcto:
fn calculate_sum(first_number: i32, second_number: i32) -> i32 { let result = first_number + second_number; result}Use nombres significativos: user_name en lugar de un, process_data en lugar de pd. Evite abreviaturas, a menos que sean de uso común (por ejemplo, idx para índice).
2. Funciones pequeñas y responsabilidad única
El Principio de Responsabilidad Única (Single Responsibility Principle) establece: cada función debe hacer solo una cosa. Si una función realiza varias acciones, es difícil de probar y modificar.
Ejemplo incorrecto (la función hace demasiado):
fn process_user(user_id: u32) { // Obtener usuario de la BD // Validar datos // Enviar email // Registrar println!("Processing user {}", user_id);}Ejemplo correcto (división en funciones pequeñas):
fn get_user(user_id: u32) -> User { // Lógica para obtener usuario User { id: user_id }}
fn validate_user(user: &User) -> bool { // Lógica de validación user.id > 0}
fn send_email(user: &User) { // Lógica para enviar email println!("Email sent to user {}", user.id);}
fn process_user(user_id: u32) { let user = get_user(user_id); if validate_user(&user) { send_email(&user); }}Procure que cada función tenga entre 10 y 20 líneas. Si es más larga, divídala.
3. Manejo de errores: use Result y Option
Rust fomenta el manejo explícito de errores mediante los tipos Result<T, E> y Option<T>. Evite usar .unwrap() o .expect() en código de producción, ya que provocan pánico ante un error.
Ejemplo incorrecto (extracción peligrosa):
fn read_file(path: &str) -> String { let content = std::fs::read_to_string(path).unwrap(); // ¡Puede fallar! content}Ejemplo correcto (manejo de error):
use std::fs;
fn read_file(path: &str) -> Result<String, std::io::Error> { let content = fs::read_to_string(path)?; // Operador ? para propagar el error Ok(content)}
fn main() { match read_file("data.txt") { Ok(data) => println!("File content: {}", data), Err(e) => eprintln!("Error reading file: {}", e), }}Use el operador ? por brevedad, pero siempre maneje los errores en el nivel superior. Para errores personalizados, cree enumeraciones con el trait std::error::Error.
4. Comentarios y documentación
El código limpio debe ser autodocumentado. Los comentarios explican el por qué, no el qué. Para documentar APIs públicas, use /// (comentarios de documentación).
Ejemplo incorrecto (comentario inútil):
// Incrementamos el contador en 1counter += 1;