ProgramaciónIngeniero de software de sistemas / Desarrollador senior de Rust

¿Qué es Unsafe Rust, para qué se necesita, cuáles son sus reglas y cómo usar correctamente los bloques unsafe para minimizar riesgos?

Supere entrevistas con el asistente de IA Hintsage

Respuesta

Unsafe Rust es una extensión del subconjunto seguro de Rust que permite realizar operaciones que el compilador no puede verificar en cuanto a la propiedad, el tiempo de vida y el aliasing. Las áreas principales de aplicación son: la interacción con bibliotecas de bajo nivel, FFI, control manual de memoria, implementación de abstracciones que no se ajustan al modelo de Rust seguro.

Características clave de unsafe:

  • Para usarlo, se debe declarar explícitamente el bloque: unsafe { ... } o la función: unsafe fn some_func()
  • En un bloque unsafe se permiten operaciones inseguras: desreferenciación de punteros crudos, llamada a funciones y métodos inseguros, acceso a uniones, variables estáticas mutables, implementación de métodos de memoria no estructurada.
  • El uso de unsafe no convierte todo el código dentro del bloque en peligroso para todo el lenguaje; solo se compromete a garantizar manualmente la corrección.

Ejemplo:

let x: i32 = 10; let ptr: *const i32 = &x as *const i32; unsafe { println!("Valor: {}", *ptr); // desreferenciación de puntero crudo }

Pregunta capciosa

¿Es el código dentro de un bloque unsafe completamente inseguro y no verificado por el compilador, o el compilador aún aplica las reglas del borrow checker y otras comprobaciones?

Respuesta: No, dentro de un bloque unsafe, el compilador sigue verificando muchas reglas de Rust (por ejemplo, tipado, reglas de propiedad, sintaxis), pero permite realizar solo aquellas acciones que de otro modo no estarían permitidas. ¡No se puede desactivar completamente el borrow checker!

Ejemplo:

let mut x = 0; let r1 = &mut x as *mut i32; // Prohibido: let r2 = &mut x as *mut i32; // incluso dentro de unsafe habrá errores si se viola la mutabilidad

Ejemplos de errores reales debido a la falta de conocimiento sobre los matices del tema


Historia

En una biblioteca de archivos asincrónica, se utilizó un puntero crudo para el control de un búfer, pero se olvidaron de rastrear correctamente el tiempo de vida del búfer. Como resultado, al cerrar el archivo, el puntero se volvió "colgante" y el acceso provocó un comportamiento indefinido (la sección unsafe no salvó de use-after-free).


Historia

En una implementación propia de una estructura similar a Vec, el programador expandía manualmente el búfer a través de unsafe. Un error de desplazamiento provocó una copia incorrecta de elementos y corrupción de datos, porque no se tuvieron en cuenta la alineación y el diseño de los tipos.


Historia

En un descriptor de hilos se utilizó un puntero estático mutable (static mut) sin la sincronización adecuada. Debido a esto, en modo multihilo, se produjo accidentalmente una condición de carrera de datos, lo que provocó un fallo esporádico de la aplicación, que solo fue detectado por fuzzing.