Code Review en Rust: qué verificar para que el código no se desmorone

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Por qué la code review en Rust no es solo una verificación de llaves



Rust es un lenguaje que promete seguridad en la gestión de memoria y alto rendimiento. Pero incluso el compilador más estricto no protege contra errores lógicos, código no idiomático o el uso injustificado de unsafe. La code review en Rust es un arte. No solo miras si las llaves están cerradas. Verificas cómo el desarrollador sorteó (o no) las limitaciones del borrow checker, qué tan eficientemente usa los iteradores y si no intenta «engañar» al sistema con punteros crudos.



En este artículo, contaré en qué aspectos vale la pena centrarse al revisar código en Rust. Sin teoría innecesaria — solo consejos prácticos, ejemplos y una lista de verificación que puedes colgar en la pared (o guardar en marcadores). ¡Allá vamos!



1. Ownership, borrowing y lifetimes: la principal fuente de dolor



Lo primero que hay que verificar es si el sistema de ownership funciona correctamente. Incluso si el código compila, no significa que sea correcto. Problemas típicos:



  • Clonación excesiva. Cuando se podría usar una referencia, el desarrollador copia los datos. Esto mata el rendimiento.
  • Borrowing mutable e inmutable. Si en un mismo scope hay &mut T y &T simultáneamente — habrá un error. Pero a veces se puede reordenar el código para evitarlo.
  • Lifetimes incorrectos. Especialmente en estructuras complejas con múltiples referencias.


Ejemplo: código malo

fn process(data: &Vec<i32>) -> i32 {    let mut sum = 0;    for &x in data.iter() {        sum += x;    }    // Todo bien, pero ¿por qué pasar &Vec si se puede &[i32]?    sum}


Código bueno

fn process(data: &[i32]) -> i32 {    data.iter().sum()}


Qué verificar en la revisión: ¿no se pasa &Vec, &String o &HashMap donde basta un slice? ¿Se usa clone() sin necesidad? ¿Están correctamente colocadas las anotaciones de lifetimes?



2. Unsafe: zona roja



Los bloques unsafe son como una escotilla abierta en el Rust seguro. Deben estar justificados, ser mínimos y estar cuidadosamente documentados. En la code review, preste atención a:



  • ¿Por qué se usa unsafe? A menudo se puede prescindir de él con abstracciones seguras.
  • Corrección de los punteros crudos. ¿No se desreferencia un puntero nulo? ¿No queda una referencia colgante después de liberar memoria?
  • Invariantes. El código con unsafe debe describir explícitamente qué invariantes mantiene.


Ejemplo: unsafe peligroso

unsafe fn dangerous() {    let ptr = 0x12345 as *mut i32;    *ptr = 42; // ¿Y si en esa dirección no hay nada?}


Ese código no debería pasar la revisión. Incluso si «funciona» — no es un argumento.



Unsafe bueno

unsafe fn init_buffer(buf: &mut [u8]) {    // Estamos seguros de que buf apunta a memoria válida,    // ya que la parte llamante lo garantiza.    std::ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len());}


Qué verificar: ¿hay un comentario que justifique cada unsafe? ¿Hay pruebas que verifiquen los casos límite? ¿No se violan las garantías de seguridad?



3. Closures y estilo funcional



Rust fomenta el enfoque funcional: iteradores, closures, map, filter, fold. Pero esto puede llevar a una complejidad excesiva. En la revisión, observe:



  • ¿Se abusa de los closures? Si un closure captura muchas variables, quizás valga la pena extraer la lógica a una función separada.

Blogs

Book Recommendations