¿Salvará Rust el mundo? (II)

David García    24 mayo, 2023

Vimos en el anterior artículo los problemas de gestión de memoria manual, pero también las trabas de la gestión automática en lenguajes como Java.

Pero, ¿y si hubiera un término medio? ¿Y si pudiéramos encontrar una forma de deshacernos de las pausas del recolector de basura, pero a su vez no tener que gestionar la memoria manualmente? ¿Cómo lo hacemos?

La gestión automática depende del recolector, que se ejecuta en tiempo de ejecución, junto con el programa. La gestión manual recae en el programador, que debe realizarla en tiempo de desarrollo. Si descartamos el recolector en tiempo de ejecución y le quitamos al programador la tarea durante el desarrollo ¿Qué nos queda? Fácil: el compilador.

La tercera vía: el compilador

La tercera vía es introducir (o mejor dicho, responsabilizar) al compilador, que es otro de los elementos en juego, dentro de las opciones de gestión de memoria. Es decir, hacerle cargo de identificar quién pide memoria, cómo se usa y cuándo deja de ser usada para reclamarla.

El compilador, como elemento de la cadena de desarrollo, tiene una amplia visión de todos los elementos que componen un programa. Sabe identificar cuándo se está solicitando memoria y hace un seguimiento de la vida de un objeto porque sabe qué símbolo se está referenciando, cómo y dónde. Y por supuesto y más importante: cuándo deja de ser referenciado.

Eso es lo que hacen lenguajes de programación como Rust, cuyo modelo se basa en ownership o propiedad con las siguientes reglas:

  • Cada valor en Rust debe tener un “propietario”.
  • Solo puede existir un “propietario” a la vez. No pueden concurrir más de uno.
  • Cuando el “propietario” sale de ámbito o visibilidad, el objeto se descarta y la memoria que pudiese contener se libera adecuadamente.

Las reglas son sencillas, pero en la práctica se necesita algo de tiempo para acostumbrarse y una gran tolerancia a la frustración puesto que el compilador se interpondrá, justamente, frente a cualquier desliz que de forma inadvertida produzca la violación de las reglas antes mencionadas.

El sistema empleado por el compilador se denomina borrow checker. Básicamente, es la parte de la compilación dedicada a comprobar que se respete el concepto de propietario respecto a un objeto y que si un propietario “presta” el objeto, este préstamo se resuelva cuando cambie el ámbito (scope) y se siga manteniendo el concepto de único propietario. Ya sea porque el que recibe el objeto se hace responsable de él o porque devuelve la propiedad.

Un ejemplo:


Si observamos las quejas del compilador y el código, vemos como la variable “s” posee la propiedad de la cadena “hola”. En la línea 3, declaramos una variable denominada “t” que toma prestada (y no devuelve) la cadena “hola”.

Posteriormente, en la línea 4, hacemos que “s” agregue una nueva cadena y complete la clásica frase “hola mundo”, pero el compilador no le deja: es un error y lo hace saber.

¿Qué ha ocurrido aquí?

Entra en juego la regla de un solo propietario. El compilador ha detectado que “s” ya no posee la propiedad de la cadena, que ahora reside en “t”, por lo que es ilegal que haga uso o intente modificar el objeto que antes poseía, puesto que ahora no le pertenece.

Esto es solo lo básico, pero pretende que nos hagamos una idea de cómo funciona esta “vigilancia” de las reglas por parte del compilador.

Imagen de Freepik.

Por cierto, ¿dónde está el peaje aquí? Por supuesto, en tiempos de compilación muy dilatados en comparación con lenguaje C o incluso Java, por ejemplo.

Conclusiones

Rust apuesta por una tercera vía en cuanto a la gestión de memoria y únicamente sacrifica el tiempo de compilación, la frustración del programador mientras desarrolla y cierta curva pronunciada a la hora de acostumbrarse a las reglas de gestión de memoria (hemos ilustrado lo básico, pero Rust posee otras nociones que complementan el préstamo, como los tiempos de vida, etc.).

Sin embargo, a pesar del precio pagado (aun así, el compilador mejora los tiempos con cada nueva versión), tendremos casi la absoluta certeza de que nuestro programa presenta una ausencia de errores de memoria (y también gran parte de condiciones de carrera) que podrían causar vulnerabilidades.

¿Salvará Rust el mundo? El tiempo lo dirá, pero la recepción y adopción que está teniendo el lenguaje es notable y va a mejorar el panorama en cuanto a vulnerabilidades basadas en errores de memoria, que no es poco.

Imagen de apertura: Creativeart en Freepik.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *