La principal atracción de Rust son sus poderosas garantías estáticas acerca de comportamiento. Pero los chequeos de seguridad son conservadores por naturaleza: existen programas que son en efecto seguros, pero el compilador no es capaz de de verificar que esto sea cierto. Para escribir ese tipo de programas, debemos decirle al compilador que relaje un poco sus restricciones. Para ello, Rust posee una palabra reservada, unsafe
. El código que hace uso de unsafe
posee menos restricciones que el código normal.
Repasemos la sintaxis, y luego hablaremos de la semántica. unsafe
es usado en cuatro contextos. El primero es para marcar una función como insegura:
unsafe fn peligro_will_robinson() { // cosas peligrosas }
Todas las funciones llamadas desde FFI deben ser marcadas como unsafe
, por ejemplo. El segundo uso de unsafe
es un bloque unsafe:
unsafe { // osas peligrosas }
El tercero es para traits unsafe:
fn main() { unsafe trait Peligroso { } }unsafe trait Peligroso { }
Y la cuarta es para la impl
ementación de uno de dichos traits:
unsafe impl Peligroso for i32 {}
Es importante poder tener la capacidad de delinear código que podría posiblemente contener bugs que originen problemas graves. Si un programa Rust termina de manera abrupta (un segfault), puedes tener por seguro que es en algún lugar de las secciones marcadas como unsafe
.
Seguro, en el contexto de Rust, significa ‘no hacer nada inseguro’. Es importante saber que hay ciertos comportamientos que son probablemente indeseables en tu código, pero son expresamente no inseguros:
Rust no puede prevenir todos los tipos de problemas de software. Las cosas de la lista anterior no son buenas, pero tampoco califican como unsafe
específicamente.
En adición, los siguientes son todos comportamiento indefinido en Rust, y deben ser evitadas, incluso cuando se escribe código unsafe
:
&mut T
y &T
siguen el modelo noalias de LLVM, excepto cuando el &T
contiene un UnsafeCell<U>
. El código unsafe no debe violar esas garantías de aliasing.UnsafeCell<U>
.std::ptr::offset
(intrínseco offset
), con la excepción de un solo byte después del final lo cual es permitido.std::ptr::copy_nonoverlapping_memory
(intrínsecos memcpy32
/memcpy64
) en buffers que se solapen.false
(0) o true
(1) en un bool
.enum
que no este incluido en su definición de tipo.char
el cual es un sustituto o por encima de char::MAX
.str
.En ambos funciones y bloques unsafe, Rust te permitirá hacer tres cosas que normalmente no podrías hacer. Solo tres. Y son:
unsafe
. Esta es la habilidad mas importante.Eso es todo. Es importante destacar que unsafe
, por ejemplo, no ‘apaga el comprobador de prestamos’. Agregar de manera aleatoria unsafe
a algún código no cambia su semántica, no comenzara a aceptar algo. Pero te permitirá escribir cosas que si rompen algunas de las reglas.
También encontraras la palabra reservada unsafe
cuando escribas bindings a interfaces foráneas (no-Rust). Lo mas recomendable es escribir una segura interfaz nativa en Rust alrededor de los métodos proporcionados por la librería.
Echemos un vistazo a las tres habilidades listadas, en orden.
static mut
Rust posee una facilidad denominada ‘static mut
’ que te permite hacer estado global mutable. Hacerlo puede causar una condición de carrera, y en consecuencia es inherentemente inseguro. Para mayor detalle, dirígete a la sección static del libro.
Los apuntadores planos te permiten llevar a cabo aritmética de punteros arbitraria, y pueden causar un numero de problemas de seguridad. En algunos sentidos, la habilidad de dereferenciar un apuntador arbitrario es una de las cosas mas peligrosas que puedes hacer, mas información en su sección en el libro.
Esta ultima habilidad funciona con ambos aspectos de unsafe
: puedes solo llamar a funciones marcadas como unsafe
desde dentro de un bloque unsafe.
Esta habilidad es poderosa y variada. Rust expone algunos intrínsecos del compilador como funciones unsafe, y algunas funciones unsafe hacen bypass de algunos chequeos de seguridad, intercambiando seguridad por velocidad.
Lo repetiré de nuevo: aun y cuando puedes hacer cosas arbitrarias en bloques unsafe y funciones no significa que debas hacerlo. El compilador actuara como si tu eres el responsable estuvieses de mantener arriba todas las invariantes, así que debes ser cuidadoso!