En la última década, Óxido se ha convertido en el lenguaje elegido por las personas que desean escribir software rápido y nativo de la máquina que también tenga sólidas garantías de seguridad de la memoria.
Otros lenguajes, como C, pueden funcionar rápido y cerca de la metal, pero carecen de las características del lenguaje para garantizar que la memoria del programa se asigne y elimine adecuadamente. Como señaló recientemente la Oficina del Director Nacional Cibernético de la Casa Blanca, estas deficiencias permitir inseguridades y exploits de software con costosas consecuencias en el mundo real. Idiomas como Rust, que Ponga la seguridad de la memoria en primer lugarestán recibiendo más atención.
¿Cómo garantiza Rust la seguridad de la memoria de una manera que otros lenguajes no lo hacen? Vamos a averiguar.
Seguridad de la memoria Rust: una característica del idioma nativo
Lo primero que hay que entender acerca de las funciones de seguridad de la memoria de Rust es que no se proporcionan a través de una biblioteca o herramientas de análisis externas, cualquiera de las cuales sería opcional. Las características de seguridad de la memoria de Rust están integradas directamente en el lenguaje. No sólo son obligatorios sino que se aplican antes de que se ejecute el código.
En Rust, los comportamientos que no son seguros para la memoria no se tratan como tiempo de ejecución errores pero como compilador errores. Clases enteras de problemas, como los errores de uso después de la liberación, son sintácticamente mal en Rust. Este código no válido nunca se compila y nunca llega a producción. En muchos otros lenguajes, incluidos C o C++, los errores de seguridad de la memoria a menudo solo se descubren en tiempo de ejecución.
Esto no significa que el código escrito en Rust sea completamente a prueba de balas o infalible. Algunos problemas de tiempo de ejecución, como condiciones de carrera, siguen siendo responsabilidad del desarrollador. Pero Rust elimina de la mesa muchas oportunidades comunes para exploits de software.
Lenguajes gestionados por la memoria, como C#, Javao Pitón, libera al desarrollador casi por completo de realizar cualquier gestión manual de la memoria. Los desarrolladores pueden concentrarse en escribir código y realizar el trabajo. Pero esa conveniencia tiene otro costo, generalmente la velocidad o la necesidad de un mayor tiempo de ejecución. Binarios de óxido puede ser muy compacto, ejecutarse a la velocidad nativa de la máquina de forma predeterminada y permanecer seguro en la memoria.
Variables de óxido: inmutables por defecto
Una de las primeras cosas que aprenden los desarrolladores novatos de Rust es que todas las variables son inmutable por defecto—lo que significa que no se pueden reasignar ni modificar. Deben declararse específicamente como mutables para poder cambiarlos.
Esto puede parecer trivial, pero tiene el efecto neto de obligar al desarrollador a ser plenamente consciente de qué valores deben ser mutables en un programa y cuándo. Es más fácil razonar sobre el código resultante porque le indica qué puede cambiar y dónde.
Inmutable por defecto es distinto del concepto de constante. Una variable inmutable se puede calcular y luego almacenar como inmutable en tiempo de ejecución; es decir, se puede calcular, almacenar y luego no cambiar. Sin embargo, una constante debe ser computable en compilar tiempo, antes de que el programa se ejecute. Muchos tipos de valores (por ejemplo, los ingresados por el usuario) no se pueden almacenar como constantes de esta manera.
C++ supone lo contrario de Rust: por defecto, todo es mutable. Debes utilizar el const
palabra clave para declarar cosas inmutables. Tú podría adoptar un estilo de codificación C++ de uso const
por defecto, pero eso solo cubriría el código tú escribir. El óxido asegura todo Los programas escritos en el lenguaje, ahora y en el futuro, asumen inmutabilidad por defecto.
Propiedad, préstamos y referencias en Rust
Cada valor en Rust tiene un «propietario», lo que significa que solo una cosa a la vez, en cualquier punto del código, puede tener control total de lectura/escritura sobre un valor. La propiedad se puede ceder o «tomar prestada» temporalmente, pero el compilador de Rust realiza un seguimiento estricto de este comportamiento. Cualquier código que viole las reglas de propiedad de un objeto determinado simplemente no se compila.
Contraste este enfoque con lo que vemos en otros idiomas. En C, no hay propiedad: cualquier otra cosa puede acceder a cualquier cosa en cualquier momento. Toda la responsabilidad de cómo se modifican las cosas recae en el programador. En lenguajes administrados como Python, Java o C#, las reglas de propiedad no existen, pero sólo porque no son necesarias. El acceso a objetos y, por tanto, la seguridad de la memoria, lo gestiona el tiempo de ejecución. Nuevamente, esto tiene como costo la velocidad o el tamaño y la presencia de un tiempo de ejecución.
Vidas en Rust
Las referencias a valores en Rust no sólo tienen propietarios, sino que vidas—es decir, un ámbito para el cual una referencia dada es válida. En la mayoría del código Rust, las duraciones se pueden dejar implícitas, ya que el compilador las rastrea. Pero la duración también se puede anotar explícitamente para casos de uso más complejos. De todos modos, intentar acceder o modificar algo fuera de su vida útil, o después de que «ha salido del alcance», genera un error del compilador. Nuevamente, esto evita que clases enteras de errores peligrosos lleguen a producción con código Rust.
Los errores de uso después de la liberación o «indicadores pendientes» surgen cuando intentas acceder a algo que en teoría ha sido desasignado o ha quedado fuera de alcance. Estos son deprimentemente comunes en C y C++. C no tiene ninguna aplicación oficial en tiempo de compilación para la duración de los objetos. C++ tiene conceptos como «punteros inteligentes» para evitar esto, pero no están implementados de forma predeterminada; tienes que optar por usarlos. La seguridad del lenguaje se convierte en una cuestión de un estilo de codificación individual o un requisito institucional, no algo que el lenguaje garantice por completo.
Con lenguajes administrados como Java, C# o Python, la administración de la memoria es responsabilidad del tiempo de ejecución del lenguaje. Esto tiene el costo de requerir un tiempo de ejecución considerable y, en ocasiones, reduce la velocidad de ejecución. Rust aplica reglas de por vida antes de que se ejecute el código.
La seguridad de la memoria de Rust tiene costos
La seguridad de la memoria de Rust también tiene costos. La primera y más importante es la necesidad de aprender y utilizar el idioma en sí.
Cambiar a un nuevo lenguaje nunca es fácil y una de las críticas más comunes a Rust es su curva de aprendizaje inicial, incluso para programadores experimentados. Se necesita tiempo y trabajo para comprender el modelo de gestión de memoria de Rust. La curva de aprendizaje de Rust es un punto de discusión constante incluso entre los partidarios del idioma.
C, C++ y todos los demás tienen una base de usuarios grande y arraigada, lo cual es un argumento frecuente a su favor. También tienen mucho código existente que se puede aprovechar, incluidas bibliotecas y aplicaciones completas. No es difícil entender por qué los desarrolladores eligen usar lenguajes C: existen tantas herramientas y otros recursos a su alrededor.
Dicho esto, en la década que lleva existiendo Rust, ha adquirido herramientas, documentación y una comunidad de usuarios que facilita la puesta al día. Y la colección de «cajas» de terceros, o bibliotecas de Rust, ya es amplia y crece a diario. El uso de Rust puede requerir un período de reentrenamiento y reequipamiento, pero los usuarios rara vez carecerán de los recursos o el soporte de la biblioteca para una tarea determinada.
Aplicar las lecciones de Rust a otros idiomas
El crecimiento de Rust ha estimulado conversaciones sobre la transformación de los lenguajes existentes que carecen de seguridad de memoria para adoptar funciones de protección de memoria similares a las de Rust.
Hay algunas ideas ambiciosas, pero, en el mejor de los casos, son difíciles de implementar. Por un lado, es casi seguro que tendrían el costo de la compatibilidad con versiones anteriores. Los comportamientos de Rust son difíciles de introducir en lenguajes donde no se utilizan sin forzar una división estricta entre el código heredado existente y el código nuevo con nuevos comportamientos.
Nada de esto ha impedido que la gente lo intente. Varios proyectos han intentado crear extensiones para C o C++ con reglas sobre seguridad y propiedad de la memoria. Los proyectos Carbon y Cppfront explorar ideas en este sentido. Carbon es un lenguaje completamente nuevo con herramientas de migración para código C++ existente, y Cppfront propone una sintaxis alternativa a C++ como una forma de escribirlo de manera más segura y conveniente. Pero ambos proyectos siguen siendo prototípicos; Cppfront solo lanzado su primera versión completa en marzo de 2024.
Lo que le da a Rust su lugar distintivo en el mundo de la programación es que sus características más poderosas y notables (seguridad de la memoria y los comportamientos en tiempo de compilación que la garantizan) son parte indivisible del lenguaje; fueron incorporados y no agregados después del hecho. Acceder a estas funciones puede exigir más del desarrollador inicialmente, pero los dividendos se pagan más adelante.
Copyright © 2024 IDG Communications, Inc.