¿Qué hace mejor Rust y cuáles son los proyectos para alguien nuevo en el lenguaje que destaca estas fortalezas?

Cuando programa con Rust, generalmente no necesita administración de memoria manual. De hecho, Rust ofrece diferentes conceptos y artefactos de gestión de memoria de bajo nivel que los de C, a saber, uno de ellos es:

El modelo de propiedad

Una de las principales fuentes de bloqueos y explotaciones de software escritos en C son los punteros colgantes. Hay otros problemas con los punteros, pero centrémonos en los punteros colgantes por ahora. En la gestión de memoria manual, uno necesita liberar la memoria manualmente. Es un problema difícil.

Un ejemplo: tienes un puntero p asignado. Por supuesto, si no necesita la memoria durante todo el tiempo de ejecución del software, debe liberarla. Esto es importante para software de larga duración como servidores. Esto no suele ser difícil. Solo libera p , cuando hayas terminado con ello. ¿O no?

Pero, ¿qué sucede si necesita usar el puntero en dos lugares diferentes de su programa? ¿Asignar p a un miembro de estructura y devolverlo desde una función? Esto abre una lata de gusanos.

Cuando el que llama a la función libera la copia de p , el miembro de la estructura se convierte en un puntero colgante. Por supuesto, la persona que llama necesita saber para establecer el miembro de la estructura en NULL, pero esto se olvida a menudo.

Y listo tenemos un nuevo exploit.

Rust resuelve este problema con el modelo de propiedad. Muchas bibliotecas de C ya tienen un concepto suelto de propiedad. El propietario es responsable de liberar el puntero. Un ejemplo en FFMPEG 2.7 aquí: AVPacket Struct Reference.

Esto funciona bien, pero si ningún compilador hace cumplir las reglas, abundan los errores estúpidos. Después de todo, los programadores son humanos y cometen errores.

El compilador Rust impone algunas reglas con el modelo de propiedad. Para más detalles, lea la fuente que se proporciona a continuación. Aquí solo doy un resumen de alto nivel de los fundamentos del modelo de propiedad tal como lo he entendido como un programador en C experimentado. No soy un programador experimentado de Rust.

Se dice que los programadores tienen que pagar un precio cuando programan con Rust. Si bien no necesitan administrar la memoria manualmente, tienen que luchar con el verificador de préstamos de Rust.

Esto puede ser frustrante, esta pelea, pero cuando un programa finalmente se compila sin bloqueos inseguros, se garantiza que no habrá punteros colgando. Y Rust no necesita verificaciones en tiempo de ejecución. No hay penalizaciones de rendimiento. Todo el trabajo se realiza en tiempo de compilación. Este es el poder de la comprobación forzada de reglas de tiempo de compilación.

Por favor, nuevamente, lea la fuente acerca de lo que hace exactamente el verificador de préstamos.

Fuente: https://doc.rust-lang.org/book/o…

Después de asistir recientemente a Rustconf, diría que una de las mejores áreas para Rust en este momento es la creación de servidores (servidores web, servicios REST, microservicios, servicios de datos, lo que sea …).

Con el reciente lanzamiento de las cajas tokio-core y futures-rs, es relativamente sencillo construir servidores altamente escalables utilizando io asíncrono.

¿Por qué Rust se adapta tan bien a esto? Bueno, aparte de los beneficios de rendimiento, Rust tiene seguridad en la memoria, lo que evita una enorme clase de errores que a menudo son responsables de los problemas de seguridad (desbordamientos de búfer, etc.).

Dado que Rust 1.0 acaba de publicarse ayer, no está claro qué hace “mejor” (lo que sea que signifique), por lo que asumiré que la pregunta está en la línea de “¿Qué hace Rust mejor que los idiomas en la misma categoría?” , como C / C ++? ”

Ahora, uno de los principales puntos de venta de Rust es que logra la seguridad de la memoria sin un recolector de basura .

Considere el siguiente código C:

int calcular ()
{
int * pi = (int *) malloc (sizeof (int));
* pi = 5;
// hacer cosas con * pi
}

Si nunca has tratado con C antes, puedes estar un poco confundido con respecto a lo que está sucediendo aquí, así que déjame explicarte:

int *pi = (int*) malloc(sizeof(int));

En esta línea, estamos creando un nuevo puntero a una variable entera y asignándole (esencialmente), la dirección de memoria de un entero al que asignamos memoria en el montón usando malloc, (un puntero es una variable que contiene la dirección de memoria de otro objeto en la memoria), (la función sizeof () solo garantiza que asignemos suficiente espacio para el tipo que le pasemos).
De modo que el puntero contiene la dirección de memoria en la que se almacenarán nuestros datos en el montón.

*pi = 5;

La línea anterior hace referencia al puntero, (accede a lo que está en la dirección de la memoria, en lugar de a la dirección de la memoria en sí misma) y almacena el valor entero 5 en esa ubicación de la memoria.

Ahora, en este punto, todo puede parecer bien, ¡cuando en realidad no lo es!

¿Qué salió mal?

Bueno, si nos fijamos en esta línea:

}

– puedes darte cuenta de que la función termina de ejecutarse allí, pero pi nunca está free() `d, es decir, nunca señalamos que ya no necesitamos el espacio de memoria que reclamamos para pi llamando a` free() ` función, como tal:

free(pi);

Debido a que nunca liberamos la memoria reservada en el montón, tenemos una memory leak desagradable en nuestras manos, lo que significa que nuestra aplicación reclama más memoria para sí misma de la que realmente necesita.
Suficientes fugas y nuestra aplicación eventualmente se bloqueará, o incluso puede derribar todo el sistema, (dependiendo del sistema operativo).

“Ok, ok, lo entiendo, pero ¿cómo resuelve Rus * este problema exactamente?”, Te oigo preguntar:

Bueno, considera este código Rust:

calcular fn () {
vamos pi = Caja :: nuevo (5);
// hacer cosas con pi
}

En la segunda línea, también podríamos haber escrito esto:

let pi: Box = Box::new(5i32);

Sin embargo, la línea anterior produce exactamente el mismo resultado que el que no tiene las anotaciones de tipo explícitas, así que ahórrese el problema y aproveche al máximo la inferencia de tipo (y la eliminación automática de punteros), que tenemos en Rust.

Creo que debería ser bastante claro en este punto lo que hace el código; let pi introduzca una variable inmutable pi y le asigne un puntero a un valor en caja 5 del tipo i32 en el montón.

Lo * realmente * interesante, sin embargo, es que, como en la versión C, no he llamado nada a free(pi) , (que en realidad sería drop(pi) en Rust, pero el efecto es el mismo), para lanzar la memoria de pila asignada.

Sin embargo, tal “omisión” en Rust no causa una pérdida de memoria, ya que el compilador puede rastrear que pi * posee * la memoria del montón asignada y que cuando se sale del alcance (una vez que el bloque termina, en este caso, una vez que la función calculate() regresa, debe insertar una llamada de drop(pi) silenciosa drop(pi) como último pensamiento anterior } .

Armados con este conocimiento, ahora podemos entender lo que sucede dentro de nuestra función calculate() :

calcular fn () {
vamos pi = Caja :: nuevo (5); // asigne suficiente memoria para un i32 en el montón y devuélvale un puntero, lo que hace que pi sea el propietario de esa porción de memoria
// hacer stuf
} // pi queda fuera del alcance aquí, así que inserta drop (pi)

Debido a que pi posee esa porción de memoria, es el único responsable de desasignar esa porción de memoria también, y porque eso sucede implícitamente cuando pi está fuera de alcance, el programador no puede olvidarlo, como es posible hacerlo en C – Rust

El concepto de propiedad es muy importante en Rust:

fn add (n1: Cuadro , n2: Cuadro ) -> i32 {
* n1 + * n2
}

fn multiplicar (n1: Cuadro , n2: Cuadro ) -> i32 {
* n1 * * n2
}

fn main () {
vamos num1 = Box :: new (7i32);
sea ​​num2 = Box :: new (5i32);

println! (“{}”, add (num1, num2)); // add () posee num1 y num2
println! (“{}”, multiplica (num1, num2)); // Multiplicar () no puede usarlos, ¡porque no los posee!
}

El código anterior no funciona, porque la llamada a add () le ha transferido la propiedad de num1 y num2, lo que significa que Multiply () no puede usarlos.

Ahora, probablemente te estás diciendo a ti mismo que todo es genial, pero un poco limitante, y tendrás razón, por eso tenemos el concepto de préstamo :

fn agregar (n1: & i32, n2: & i32) -> i32 {
* n1 + * n2
}

fn multiplicar (n1: & i32, n2: & i32) -> i32 {
* n1 * * n2
}

fn main () {
vamos num1 = Box :: new (7i32);
sea ​​num2 = Box :: new (5i32);

println! (“{}”, add (& num1, & num2));
println! (“{}”, multiplica (& num1, & num2));
}

Aquí, add () solo toma prestados los argumentos que se le pasaron, en lugar de tomar posesión.
Eso significa que main () aún posee num1 y num2 y, por lo tanto, puede pasarlos a multiplicar (), ¡así que todo funciona!

Rust es un intento de un lenguaje de programación de sistemas más limpio. Cerca del hardware, pero con suficientes abstracciones para evitar las dificultades comunes de la actualidad (es decir, fugas de memoria, desbordamiento de búfer, …).