Miri: una máquina virtual para Rust
Hace poco salía la noticia de que Miri ya estaba disponible para instalar en las versiones nightly de Rust. Pero, ¿qué es Miri? y ¿por qué es importante una máquina virtual para Rust?
Rust es un lenguaje compilado a código máquina. No necesita intérpretes ni máquinas virtuales para funcionar, sin embargo, hay situaciones donde Miri es útil. Hay dos casos en particular donde es interesante y se habilita la posibilidad de un tercero:
- Evaluar las expresiones const de forma más eficiente
- Detectar fallos que solo pueden ser detectados en runtime
- Permitirá tener un REPL de Rust en el futuro
¿Cómo? Las expresiones const son código Rust que se ejecuta en tiempo de compilación, para obtener una constante que finalmente será la que vaya en el ejecutable. Cuando rustc se encuentra con una de ellas, compila el código necesario para obtener el valor de vuelta de esa función. Este proceso es largo, sobre todo teniendo en cuenta que ese código solo va a ser ejecutado durante la compilación del programa entero, por lo que no es necesaria una compilación completa a código máquina. De este modo se mejora la velocidad en la mayoría de expresiones const.
Por otro lado, Rust no es capaz de prevenir todos los fallos posibles dada su naturaleza y en código unsafe todavía menos. Miri comprueba los siguientes fallos:
- Accesos fuera de memoria y use-after-free
- Uso incorrecto de variables no inicializadas
- Violación de precondiciones intrínsecas (llegar a
unreachable_unchecked
, llamarcopy_nonoverlapping
con rangos que se superponen, ...) - Accesos a memoria no lo suficientemente bien alineados
- Violaciones de invariantes de tipos básicos (por ejemplo un bool que no sea ni 0 ni 1 internamente o un valor de enum fuera de rango)
En este sentido Miri aporta algo similar a Valgrind de C pero mejorado.
Actualmente Miri tiene algunas limitaciones, por ejemplo, no soporta concurrencia, acceso al sistema de archivos o acceso a la red.
¿Cómo usar Miri?
Miri está disponible solo en algunas versiones nightly. Al tiempo de escribir este post, los siguientes comandos permiten obtener la copia más reciente de Miri:
rustup toolchain install nightly-2019-04-17
rustup component add miri --toolchain=nightly-2019-04-17
cargo +nightly-2019-04-17 miri test
cargo +nightly-2019-04-17 miri run
El uso es muy sencillo, test ejecuta los test y run ejecuta la aplicación si es un binario.
Pongamos un ejemplo de un test que funciona normalmente pero falla en Miri.
#[test]
fn does_not_work_on_miri() {
let x = 0u8;
assert!(&x as *const _ as usize % 4 < 4);
}
Ejecutando cargo test todo funciona bien pero con miri test falla.
Y con esto ya tenemos otra herramienta más para nuestra programación en Rust