Aprendiendo Rust por primera vez

26 noviembre, 2015 3:25 por

Este artículo es una traducción del original publicado en el blog de Mozilla Hacks. Traducción por Gerardo Cruz.Logo Rust
Rust es un nuevo lenguaje de programación que se centra en el rendimiento, la paralelización, y la seguridad de la memoria. Con la construcción de un lenguaje a partir de cero y la incorporación de elementos de diseño del lenguaje de programación moderno, los creadores de Rust evitaron una gran cantidad de “legado” (requisitos de compatibilidad con versiones anteriores) que los lenguajes tradicionales tienen que tratar. En cambio, Rust es capaz de fusionar la sintaxis expresiva y flexibilidad de lenguajes de alto nivel con el control sin precedentes y el rendimiento de un lenguaje de bajo nivel.

La elección de un lenguaje de programación por lo general implica ventajas y desventajas. Aunque la mayoría de los lenguajes modernos de alto nivel proporcionan herramientas para la seguridad de concurrencia y la seguridad de la memoria, lo hacen con una sobrecarga añadida (por ejemplo, mediante el uso de un GC (recolector de basura)), y tienden a carecer de rendimiento y control refinado.

Para hacer frente a estas limitaciones, se tiene que recurrir a lenguajes de bajo nivel. Sin las redes de seguridad de la mayoría de los lenguajes de alto nivel esto puede ser frágil y propenso a errores. Uno tiene repentinamente para hacer frente a la gestión manual de memoria, asignación de recursos, los punteros colgantes, etc. Crear software que puede aprovechar el creciente número de núcleos presentes en los dispositivos de hoy en día es difícil – asegurándose de que dicho código funciona correctamente es aún más difícil.

Entonces, ¿cómo incorpora Rust lo mejor de ambos mundos en un solo idioma? Eso es lo que te mostraremos en este artículo. Rust 1.0.0 estable acaba de ser lanzado. El idioma ya tiene una comunidad vibrante, un creciente ecosistema de cajas (bibliotecas) en su gestor de paquetes, y desarrolladores que aprovechan sus capacidades en una variedad de proyectos. Incluso si nunca has tocado un lenguaje de bajo nivel, ¡éste es el momento perfecto para aprender!

T-shirts Rust

T-shirts de Rust que celebran el lanzamiento 1.0

Los casos de uso para Rust hoy

Así que para los programadores de sistemas Rust parece una gran opción, pero ¿qué hay de aquellos que no están familiarizados con lenguajes de bajo nivel? Tal vez la última vez que escuchaste sobre  “C” y “asignación de pila / heap” fue hace 10 años en el primer curso de ciencias de la computación (o tal vez nunca). Rust ofrece el rendimiento normalmente visto sólo en los idiomas de sistemas de bajo nivel – pero la mayoría de las veces, ¡sin duda se siente como un lenguaje de alto nivel! Aquí hay algunos ejemplos de cómo puedes aprovechar Rust ahora en aplicaciones prácticas:

Quiero hackear en hardware / aplicaciones de “Internet de las cosas”

La era Internet de las cosas (IoT) y la expansión del movimiento maker permite una democratización real de proyectos de hardware. Ya se trate de la Raspberry Pi, Arduino, o uno de los jóvenes titanes como el BeagleBone o Tessel, puedes elegir entre una gran cantidad de lenguajes para codificar tus proyectos de hardware, incluyendo Python o JavaScript.

Hay veces, sin embargo, cuando el rendimiento que ofrecen estos lenguajes no es adecuado. En otras ocasiones, el microcontrolador de hardware que pretendemos soportar no es adecuado para los ambientes de ejecución que estos lenguajes requieren: chips lentos con reservas de memoria pequeñas y aplicaciones de ultra bajo consumo de energía todavía requieren un lenguaje más cerca al metal. Tradicionalmente este lenguaje ha sido C – pero como ya habrán adivinado, Rust es el nuevo chico en el bloque.

Rust es compatible con una amplia variedad de plataformas exóticas. Mientras que algo de esto es todavía experimental, el soporte incluye hardware ARM genérico, el tablero de desarrollo TIVA de Texas Instruments, e incluso el Raspberry Pi. Algunas de las nuevas tablas de IoT como el Tessel 2, ¡vienen con soporte oficial para Rust!

Estoy operando aplicaciones de computación de alto rendimiento que escalan a múltiples núcleos

Los estudios sugieren que Rust ya es bueno para HPC (computación de alto rendimiento).  Incluso no tienes que reescribir toda tu aplicación en Rust: su flexible interfaz para funciones foráneas (FFI) proporciona enlaces de C eficientes que te permiten exponer y llamar código Rust sin ninguna sobrecarga notable. Esto te permite volver a escribir tu aplicación módulo por módulo, pasando lentamente hacia una mejor experiencia del desarrollador que se traducirá en rendimiento equivalente al código antiguo o mejor. También conseguirán una base de código más fácil de mantener con menos errores, que escala mejor en un elevado número de núcleos.

¡Simplemente necesito algo rápido!

Rust es ideal para reescribir partes sensibles al rendimiento de tu aplicación. Se conecta bien con otros lenguajes a través de FFI y tiene un pequeño tiempo de ejecución que compite con C y C ++ en la mayoría de los casos, incluso cuando los recursos son limitados.

A pesar de la naturaleza de trabajo en progreso del lenguaje, ya hay aplicaciones críticas de negocio, en vivo en producción que han estado haciendo un buen uso de Rust desde hace algún tiempo: el startup de Yehuda Katz, Skylight, utiliza código Rust de alto rendimiento incrustado en un gem de Ruby para procesamiento de datos. La versión 1.0.0 estable es también un hito importante en que no deberían ocurrir  cambios de ruptura de compatibilidad a partir de ahora. ¡Eso hace que sea seguro recomendar Rust incluso para las aplicaciones más exigentes y robustas!

Ve a Yehuda Katz y Tom Dale hablar de los fundamentos de la programación Rust y cómo utilizaron Rust con Ruby en su aplicación.

Empezando con Rust

Hay muchos tutoriales que cubren Rust en general, así como los aspectos específicos del lenguaje. Por ejemplo, el blog de ​​Rust tiene grandes artículos sobre diversos aspectos de desarrollo de aplicaciones Rust. También hay excelentes charlas introductorias como la charla de Aaron Turon en la Universidad de Stanford. Explica los principales conceptos y motivaciones detrás de Rust muy bien, y la charla sirve el aperitivo perfecto para empezar tu recorrido:

Charlas y tutoriales no son un sustituto para la escritura de código, ¿no? ¡Rust tiene todo cubierto en ese sentido, también! El libro de Rust fue concebido para ayudarte a empezar – desde la instalación, al primer Hello World, hasta proporcionar una referencia detallada de todas las características lingüísticas esenciales.

Otro recurso digno de mencionar es Rust by Example, que te guía a través de las características clave de Rust, desde lo más básico a los poderes arcanos de rasgos (traits), macros y la FFI. Este recurso ofrece ejemplos invaluables, editables en vivo en el navegador para todos los artículos. Incluso no tienes que descargar y compilar Rust, ya que se puede probar y editar todos estos ejemplos desde la comodidad de tu sofá de la sala, tal y como son. También está el Rust Playpen para exactamente el mismo propósito.

No es que la descarga e instalación Rust es mucho problema, de todos modos. Dirígete a la página de inicio y descarga el binario / instalador apropiado allí. Las descargas contienen todas las herramientas que necesitas para empezar (como el compilador rustc,  o el gestor de paquetes cargo), y están disponibles pre-construidos para todas las plataformas (Windows, Linux y OSX).

¿Qué es entonces, lo que hace Rust tan único, tan diferente?

El camino Rust

Podría decirse que es el modelo de propiedad único de Rust el que le permite brillar de verdad – la eliminación de clases enteras de errores relacionados con administración de threads y la gestión de memoria, mientras que facilita el desarrollo y la depuración. También es muy valioso para mantener un ambiente de ejecución mínimo y la salida del compilador liviana y con buen desempeño.

El modelo de propiedad

El principio básico del modelo de propiedad de Rust es que cada recurso puede pertenecer a un y sólo un “dueño”. Un “recurso” puede ser cualquier cosa – de un segmento de memoria a un socket de red abierto a algo más abstracto como un bloqueo de exclusión mutua. El propietario de dicho recurso es responsable de usar, posiblemente prestar el recurso (a otros usuarios), y finalmente limpiarlo.

Todo esto sucede de forma automática, con la ayuda de rangos (scopes) y asignaciones (bindings): las asignaciones son propietarias de valores o los piden prestados, y persisten hasta el final de su rango. Cuando las asignaciones se salen de su rango, o bien dan su recurso prestado de vuelta, o lo eliminan si eran propietarias.

Lo que es aún mejor es que los controles necesarios para que el modelo de propiedad funcione son ejecutados y aplicados por el compilador Rust y el “revisor de préstamos” durante la compilación, que a su vez se traduce en diversos beneficios.

Abstracciones de cero costo

Ya que el correcto funcionamiento de un programa se afirma en tiempo de compilación, el código que compila en general puede considerarse seguro en cuanto a los tipos más comunes de errores de memoria y de concurrencia (como defectos use-after-free o carreras de datos). Esto es posible porque Rust se queja en tiempo de compilación si el programador intenta hacer algo que viola los principios anteriores, que a su vez ayuda a mantener el ambiente de ejecución al mínimo.

Al delegar estos controles al tiempo de compilación, no hay necesidad de realizarlos en tiempo de ejecución (el código ya es “seguro”), y no hay sobrecarga de tiempo de ejecución. En el caso de las asignaciones de memoria, no hay necesidad de un recolector de basura de tiempo de ejecución, tampoco. Esto – construcciones avanzadas del lenguaje con poca o ninguna sobrecarga de tiempo de ejecución – es la idea básica de “abstracciones de cero costo”.

Aunque una explicación detallada del modelo de propiedad se encuentra fuera del alcance de este artículo, el Libro de Rust (vinculado arriba) y varias charlas y artículos sobresalen en la explicación de sus principios. Los principios de apropiación y el revisor de préstamos son esenciales para la comprensión de Rust, y son claves para otros aspectos de gran alcance del lenguaje, al igual que su manejo de la concurrencia y paralelismo.

Dos pájaros, una construcción del lenguaje

El estado mutable compartido es la raíz de todos los males. La mayoría de los lenguajes tratan de hacer frente a este problema a través de la parte mutable, pero Rust trata con él mediante la resolución de la parte compartido.

El libro Rust

Aparte de la seguridad de la memoria, paralelismo y simultaneidad son el segundo foco más importante de la filosofía de Rust. Puede parecer sorprendente, pero el sistema de propiedad (junto con algunos rasgos útiles) también proporciona potentes herramientas para manejo de threads, sincronización y acceso a datos concurrente.

Al intentar algunas de las primitivas incorporadas de concurrencia y empezar a cavar más profundo en el lenguaje, puede ser una sorpresa que esas primitivas son proporcionados por la biblioteca estándar, en lugar de ser parte del propio núcleo del lenguaje. Por lo tanto, dar con nuevas formas de hacer la programación concurrente está en las manos de autores de bibliotecas – en lugar de estar predeterminados en Rust, limitados por los planes de futuro de los creadores del lenguaje.

¿Rendimiento o expresividad? ¡Elige los dos!

Gracias a su sistema de tipos estático, características cuidadosamente seleccionadas, y que no tiene recolector de basura, Rust compila en código rápido y predecible que estpa a la par con el código escrito en lenguajes tradicionales de bajo nivel (como C/C++).

Moviendo los diferentes controles del tiempo de ejecución y la eliminación de la recolección de basura, el código resultante imita las características de rendimiento de otros lenguajes de bajo nivel, mientras que el lenguaje mismo se mantiene mucho más expresivo. Los (aparentemente) elementos de alto nivel (cadenas, colecciones, funciones de orden superior, etc.) en general evitan el golpe de rendimiento en tiempo de ejecución comúnmente asociados con ellos. Dado que la salida del compilador de Rust se encuentra en la representación intermedia LLVM, el código de máquina final hace un buen uso de los beneficios multi-plataforma del compilador LLVM y todas las optimizaciones adicionales (actuales y futuras) de forma automática.

Podríamos seguir durante horas sobre cómo con Rust nunca tendrás que preocuparte de cerrar sockets o liberar memoria, cómo los rasgos y macros le dan una flexibilidad increíble para la sobrecarga de operadores, o incluso trabajar con el aspecto funcional de Rust, manipular iteradores, closures y funciones de orden superior . ¡Pero no queremos abrumarte desde el principio! Tarde o temprano te encontrarás con ellos de todos modos – y es más divertido descubrir por ti mismo exactamente qué tan profundo es el hoyo del conejo.

Así que ahora en cambio, vamos finalmente a ver algo de código.

Dile hola a Rust

Sin más preámbulos – tu primer código Rust:

fn main() {
    println!("Hello, Rust!");
}

Abrir este código en Rust Playpen >>

Espera, ¿eso es todo? Esto puede parecer un poco anti-climático, pero sólo hay tantas formas de escribir un Hello World en Rust. Aguántanos un poco mientras compartimos un ejemplo un poco más complejo – y otro clásico.

¿Qué es todo esto del FizzBuzz?

¡Vamos a la antigua con el programa FizzBuzz! FizzBuzz es un algoritmo en el que se cuenta hacia arriba por toda la eternidad, sustituyendo de algunos de los números con fizz (para los números divisibles por 3), buzz (para los números divisibles por 5), o fizzbuzz (divisible por tanto 3 y 5).

Además de mostrar nuestro punto (y para ayudarle a familiarizarte con la semántica de Rust), hemos incluido las versiones C y Python del mismo algoritmo:

FizzBuzz Imperativo – versión C

#include 

int main(void)
{
    int num;
    for(num=1; num<101; ++num)
    {
        if( num%3 == 0 && num%5 == 0 ) {
            printf("fizzbuzz\n");
        } else if( num%3 == 0) {
            printf("fizz\n");
        } else if( num%5 == 0) {
            printf("buzz\n");
        } else {
            printf("%d\n",num);
        }
    }

    return 0;
}

FizzBuzz Imperativo – versión Python

for num in xrange(1,101):
    if num%3 == 0 and num%5 == 0:
        print "fizzbuzz"
    elif num%3 == 0:
        print "fizz"
    elif num%5 == 0:
        print "buzz"
    else:
        print num

FizzBuzz Imperativo – Versión Rust

fn main() {
    for num in 1..101 { // notación de rangos!
        match (num%3, num%5) { // reconocimiento de patrones!
            (0, 0) => println!("fizzbuzz"),
            (0, _) => println!("fizz"),
            (_, 0) => println!("buzz"),
                 _ => println!("{}", num) 
        }
    }
}

Abrir este código en Rust Playpen >>

Incluso en un pequeño fragmento de este tipo, algo del poder de Rust se comienza a mostrar. La diferencia más obvia podría ser que estamos usando reconocimiento de patrones en lugar de los tradicionales if. Nosotros podríamos usarlos, sin embargo el match es una adición muy útil al arsenal de un Rustacean.

La otra cosa que cabe notar es la notación de rango en el ciclo for (similar a su contraparte de Python). Es interesante, sin embargo, que en este caso no podríamos haber utilizado un “tradicional” ciclo for C – dado que no está soportado en Rust por diseño. Los ciclos for tradicionales se consideran propensos a errores y se sustituyen con el más seguro y más flexible concepto de iterables.

He aquí un vistazo más elaborado de lo que sucede en la fase de reconocimientos de patrones de nuestro ejemplo FizzBuzz:

...
 
// Para reconocimento de patrones, construimos una tupla
// con los residuos de la división entera de num por 3 y 5
match (num%3, num%5) {
    // Cuando num es divisible por 3 Y 5
    // (ambos residuos son  0)
    // -> imprimir "fizzbuzz"
    (0, 0) => println!("fizzbuzz"),

    // Cuando num es divisible por 3 (el residuo es 0)
    // ¿Es num divisible por 5? -> no nos interesa
    // -> imprimir "fizz"
    (0, _) => println!("fizz"),

    // Cuando num es divisible por 5 (el residuo es 0)
    // ¿Es num divisible por 3? -> no nos interesa
    // -> imprimir "buzz"
    (_, 0) => println!("buzz"),

    // En los otros casos, solo imprimir el valor de num
    // Nota que la comparación debe ser exhaustiva (o sea,
    // cubrir todos los casos posibles) - ¡esto es revisado
    // por el compilador!
         _ => pintln!("{}", num)
}
...

Este no es el lugar para profundizar en cómo funciona el reconocimiento de patrones, o la desestructuración, o qué son las tuplas. Encontrará excelentes artículos sobre estos temas en el libro de Rust , el blog de Rust, o más en Rust By ExamplePor, pero pensamos que es una buena manera de demostrar las características y matices que hacen Rust tanto potente como eficaz.

¿Qué tal algo de FizzBuzz funcional?

Para expandir el último ejemplo y demostrar la verdadera flexibilidad de Rust, en nuestro segundo ejemplo vamos a subir nuestro FizzBuzz a otro nivel. Rust se autodenomina como multi-paradigma. Podemos poner esto a prueba al reescribir nuestro ejemplo FizzBuzz en estilo funcional. Para ayudar a poner el código en perspectiva, también vamos a mostrar la implementación en JavaScript (el sabor ECMAScript 6), también:

FizzBuzz Funcional – JavaScript versión (ES6)

Array.from(Array(100).keys()).slice(1)
    .map((num) => {
        if (num%3 === 0 && num%5 === 0) {
            return "fizzbuzz";
        } else if (num%3 === 0) {
            return "fizz";
        } else if (num%5 === 0) {
            return "buzz";
        } else {
            return num;
        }
    })
    .map(output => console.log(output));

FizzBuzz Funcional – versión Rust

fn main() {
    (1..101)
        .map(|num| {
            match (num%3, num%5) { // reconocimento de patrones!
                (0, 0) => "fizzbuzz".to_string(),
                (0, _) => "fizz".to_string(),
                (_, 0) => "buzz".to_string(),
                     _ => format!("{}", num) 
            }
        })
        .map(|output| { println!("{}", output); output })
        .collect::>();
}

Abrir este código en el Rust Playpen >>

No sólo podemos imitar el estilo funcional de JavaScript con closures y llamadas a métodos funcionales, pero gracias al poderoso reconocimiento de patrones de Rust, incluso podemos ganarle un poco a JavaScript en expresividad.

Nota: Los iteradores de Rust son lazy (pasivos), así que en realidad necesitas la llamada a .collect() (lo que se llama un consumidor) para que esto funcione – si no la incluyes, nada del código anterior se ejecutará. Echa un vistazo a la sección de consumidores del libro de Rust para aprender más.

Escogiendo un proyecto

Tienes ganas de sumergirse en Rust y listo para comenzar algo bueno. ¿Pero qué construir? Podrías comenzar con algo pequeño y escribir una aplicación pequeña o una pequeña biblioteca, y subirlo a crates.io. La gente ha creado muchos proyectos interesantes en Rust ya – kernels *nix, proyectos de hardware empotrado, desde juegos hasta frameworks de servidor web, lo que demuestra que el único límite es tu imaginación.

Si quieres empezar poco a poco, pero aportar algo útil mientras aprendes las complejidades de la programación Rust, considera participar en algunos de los proyectos que ya prosperan en GitHub (o incluso el propio compilador Rust – que también está escrito en Rust).

O bien, puedes empezar por volver a escribir pequeñas partes de tu aplicación de producción, y experimentar con los resultados. El FFI de Rust pretende ser un reemplazo drop-in para el código C. Con un mínimo esfuerzo podrías reemplazar pequeñas partes de tu aplicación con las implementaciones Rust del mismo código. Pero la interoperabilidad no se detiene allí – estos enlaces C funcionan en ambos sentidos. Esto significa que puedes utilizar todas las bibliotecas C estándar en tus aplicaciones Rust – o (ya que muchos lenguajes de programación vienen con bibliotecas de interfaz de C estándar) realizar llamadas de ida y vuelta entre Rust y Python, Ruby, ¡o incluso Go!

Mira la charla de Dan Callahan “Mi Python es un poco Rusty” sobre cómo conectar Rust con Python.

Prácticamente no hay lenguajes que no funcionan con Rust: ¿quieres compilar Rust a JavaScript o usar ensamblador en línea en el código? ¡Sí puedes!

Ahora sale y crea algo impresionante.

Y no olvides – Rust no es simplemente un lenguaje de programación – es también una comunidad. Si alguna vez te siente atrapado, no seas tímido para pedir ayuda, ya sea en los foros o IRC. También puedes compartir tus pensamientos y creaciones en el Reddit de Rust.

Rust ya es grande – ¡es la gente como que va a hacer que sea aún más impresionante!

The following two tabs change content below.

jorgev

AMO Product Manager at Mozilla
Jorge trabaja para el equipo de complementos de Mozilla, y se dedica a Mozilla Hispano y Mozilla Costa Rica en su tiempo libre. Actualmente está encargado del blog de Mozilla Hispano Labs.

Compartir artículo:

Notable Replies

Continue the discussion foro.mozilla-hispano.org

Participants

  • ¡Participa!

    Firefox Friends »
    Agrega botones de Firefox en tu sitio web y comparte tu amor por Mozilla Firefox.
    Ayuda a otros usuarios en Twitter.
    Colabora con la comunidad »
    En Mozilla lo importante son las personas. Descubre cómo puedes colaborar.

    Boletín Firefox

    Suscríbete al boletín de novedades de Firefox.

  • Descargas

    Descarga los programas de Mozilla.

    Lo más visto

    cc-by-sa