Primeros pasos con Rust y porque aprenderlo

mattdark

Mario García

Posted on January 17, 2019

Primeros pasos con Rust y porque aprenderlo

Rust es un lenguaje de programación del que mucho se habla en estos días, tan es así que la última encuesta de desarrollo de Stack Overflow coloca a Rust como el lenguaje más querido por tercer año consecutivo. Además, se trata el lenguaje de programación que Mozilla le está dando al mundo, que inició su desarrollo en 2009 y cuya primera versión estable se publicó el 15 de Mayo de 2015, y por el que muchas empresas y desarrolladores están apostando.

Un lenguaje de programación de sistemas enfocado principalmente en los siguientes aspectos:

  • Seguridad
  • Velocidad
  • Concurrencia

Lo cual logra con la implementación de buenas prácticas que serán descritas un poco más adelante y algunas otras en un próximo artículo, prácticas que puedes aplicar a otros lenguajes o tecnologías. El no incluir un recolector de basura, el soporte de la programación concurrente, el uso de óptimo de memoria con la declaración de variables sólo cuando se necesita, la introducción de conceptos como ownership.

Compilado, con soporte para múltiples sistemas operativos (GNU/Linux, MacOS, Windows) y arquitecturas (x86, x86_64, ARM), tal y como puede verse en la documentación.

Un soporte que ha mejorado mucho en los últimos años. Como usuario de Manjaro (distro derivada de Arch Linux) he visto como la instalación de Rust en estas distribuciones es más sencilla que cuando empecé a aprender Rust a principios de 2016, pues el instalador (rustup, del que les contaré más adelante) está disponible desde los repositorios oficiales.

En dispositivos como Raspberry Pi, en donde se tenía que hacer una compilación cruzada para instalar Rust, hoy solo basta ejecutar el comando de instalación que indican en el sitio oficial o descargar desde los repositorios oficiales dependiendo de la distribución, como en el caso de Arch Linux ARM.

Su integración con otros lenguajes y tecnologías hace de Rust un lenguaje a considerar, aprovechando las ventajas y características de este. Node.js + Rust con Neon para crear módulos nativos de Node.js, Python + Rust usando PyO3, y su uso para desarrollo backend en aplicaciones web y de escritorio.

Un lenguaje que está generando interés en la comunidad global de desarrollo, con empresas que apuestan por el lenguaje, incluyendolo en su stack de tecnologías, como Canonical, Dropbox y Coursera. Mozilla que usa Rust para desarrollar Servo, el motor de renderizado introducido en Firefox Quantum, proyectos de Software Libre como GNOME que están migrando parte del código del escritorio a Rust.

Desarrolladores en América Latina, Estados Unidos, países de Europa y Asia que están apostando por Rust, lo que también hace que cada vez se realicen más eventos, incluyendo meetups como los de Ciudad de México, Barcelona. Conferencias como el Rust Fest en Europa, Rust Latam, primera conferencia regional para América Latina que se realizará en Marzo de este año en Montevideo, Uruguay, RustConf en Estados Unidos, RustRush en Rusia. Hello, Rust!, uno de los canales de YouTube en los que se habla del lenguaje.

Una lista de proyectos interesantes que están siendo desarrollados con el lenguaje y que crece muy rápido y en la que pueden encontrarse desde sistemas operativos como Redox, frameworks para desarrollo web como Rocket, Iron. Soporte en editores de texto entre los que se incluyen Atom, Emacs, Nano, etc. Lista que puede consultarse en GitHub.

Ese mismo interés ha hecho que hoy existan más libros de Rust como The Rust Programming Language de Steve Klabnik y Carol Nichols. Y algunos otros siendo escritos en este momento, o los ya publicados de los que se ha hecho eco la cuenta oficial de Rust en Twitter.

Con una mascota como Ferris, que hoy podemos ver en camisetas|playeras|remeras, stickers y hasta peluches.

Ferris

Instalación

Para instalar Rust, los usuarios de GNU/Linux y MacOS deben ejecutar el siguiente comando desde la terminal:

$ curl https://sh.rustup.rs -sSf | sh
Enter fullscreen mode Exit fullscreen mode

El comando anterior realiza la descarga de rustup (instalador de Rust) e instala la versión estable del lenguaje, que incluye el compilador (rustc) y Cargo (gestor de paquetes).

En Arch Linux y derivados, rustup está disponible desde los repositorios oficiales por lo que puede instalarse usando pacman:

$ sudo pacman -S rustup
Enter fullscreen mode Exit fullscreen mode

Para Windows y otros sistemas operativos, se puede descargar el instalador correspondiente desde el sitio de rustup y seguir las indicaciones.

Herramientas

rustup

rustup es el instalador de Rust y la herramienta con la que pueden gestionarse las diferentes versiones de Rust instaladas. El lenguaje sigue un ciclo de desarrollo muy similar al de Firefox, por lo que cada seis semanas se publica una versión nueva. La última versión estable es la 1.31.1, qué es la primera actualización menor de Rust 2018 (Rust 1.31), publicada el 20 de Diciembre.

En Rust existen tres canales de actualización: Stable, Beta y Nightly. Algunos frameworks como Rocket (un framework para desarrollo web) ocupan la versión Nightly.

Comandos

Instalar

$ rustup install nightly
Enter fullscreen mode Exit fullscreen mode

Actualizar
Todas las versiones instaladas

$ rustup update
Enter fullscreen mode Exit fullscreen mode

Una versión específica

$ rustup update nightly
Enter fullscreen mode Exit fullscreen mode

Especificar versión por defecto

$ rustup default stable
Enter fullscreen mode Exit fullscreen mode

Especificar versión para un proyecto

$ rustup override set nightly
Enter fullscreen mode Exit fullscreen mode

rustc

Se trata del compilador del lenguaje. Los archivos que contienen código fuente de Rust llevan la extensión .rs, y su estructura es la siguiente:

fn main() {
      println!("Hello, world!");
  }
Enter fullscreen mode Exit fullscreen mode

Se guarda el código anterior en un archivo llamado hello_world.rs, o el nombre que se le quiera dar, y para ejecutar el programa se escriben los siguientes comandos en la terminal, desde la ruta donde se haya guardado el archivo:

$ rustc main.rs
$ ./main
Hello, world!
Enter fullscreen mode Exit fullscreen mode

En la primera línea se compila el código fuente, se genera un binario con o sin extensión dependiendo del sistema operativo, y en el caso de GNU/Linux este se ejecuta como se muestra en la segunda línea, la salida del programa aparece como se ve en la tercera línea.

Usando rustc se puede comprobar la versión que se tenga instalada de Rust:

$ rustc --version
Enter fullscreen mode Exit fullscreen mode

que muestra lo siguiente en la terminal:

rustc 1.31.1 (b6c32da9b 2018-12-18)
Enter fullscreen mode Exit fullscreen mode

Cargo

Cargo es el sistema de construcción y gestor de paquetes de Rust, una herramienta que permite crear un proyecto y gestionar las dependencias (crates) del mismo.

Nuevo proyecto

$ cargo new hello_world
Enter fullscreen mode Exit fullscreen mode

El comando anterior crea un proyecto nuevo usando Cargo, hello_world es el nombre del proyecto y de la carpeta que genera, la cual tiene la siguiente estructura:

~/hello_world$ ls -a
Cargo.toml  src  .git  .gitignore
Enter fullscreen mode Exit fullscreen mode

Cargo.toml es el manifiesto del programa, contiene información general del proyecto. El directorio src contiene un archivo con código fuente de Rust generado automaticamente. Además, se inicializa un repositorio Git.

Cargo.toml
La estructura de este archivo es la siguiente:

[package]
name = ”hello_world”
version = 0.1.0
authors = [“mattdark”
edition = 2018

[dependencies]
Enter fullscreen mode Exit fullscreen mode

En la sección [package] se tiene información general del proyecto, que incluye nombre, versión, autor y edición.

El campo author se refiere al autor o autores del programa y en el caso de GNU/Linux este dato lo toma del usuario del sistema. El campo edition se agregó a partir de Rust 1.31, versión publicada el 6 de diciembre, y se ocupa para indicar la edición de Rust usada en el desarrollo.

Existen dos ediciones de Rust, 2015 y 2018. 2015 hace referencia a todas las versiones de Rust entre 1.0 y 1.30. 2018 que es la primera actualización importante publicada desde el lanzamiento de la versión 1.0. Con algunos cambios significativos en la sintaxis y funcionalidades agregadas, como se describió en el artículo publicado en el blog.

Cargo crea el archivo main.rs dentro del directorio src y este contiene las siguientes líneas de código:

fn main() {
      println!("Hello, world!");
  }
Enter fullscreen mode Exit fullscreen mode

Para ejecutar el programa con Cargo, se escriben los siguientes comandos en la terminal:

$ cargo build
$ cargo run
Enter fullscreen mode Exit fullscreen mode

El primer comando realiza la compilación, comprueba las dependencias del proyecto, y descarga y compila las crates correspondientes. Este comando es opcional.

El segundo comando ejecuta el binario generado durante la compilación y además realiza el proceso de compilación previo a la ejecución.

Sintaxis de Rust

En la sección anterior aparece en dos ocasiones el siguiente bloque de código, que es un ejemplo de ‘Hola mundo’ en Rust:

fn main() {
      println!("Hello, world!");
  }
Enter fullscreen mode Exit fullscreen mode

El código fuente de Rust se coloca dentro de una función principal (main) que se define usando la palabra reservada fn. Se usan llaves ({}) y punto y coma (;) como parte de la sintaxis, tal y como sucede en C/C++ o JavaScript.

Para imprimir texto se usa la macro println como se muestra en la segunda línea. En Rust las funciones definidas como parte de la sintaxis del lenguaje se conocen como macros, y se distinguen de otras funciones por llevar un signo de admiración (!) después del nombre de la función.

Imprimir texto

Para imprimir texto se usa la macro println y su sintaxis es la siguiente:

println!(string {} literal, expressions);
Enter fullscreen mode Exit fullscreen mode

println es la macro que se usa para imprimir texto, como se vio en el ejemplo de ’Hola mundo’, puede usarse también para imprimir el resultado de una operación o el valor de una variable. Puede recibir dos o más parámetros, por ejemplo:

println!("Hola, {}!", "mundo");
Enter fullscreen mode Exit fullscreen mode

En el ejemplo anterior println recibe parte del mensaje que se imprimirá, las llaves se usan para indicar que el siguiente valor a mostrar es el de una variable o valor pasado como segundo parámetro, en este caso la palabra ‘mundo’.

Comentarios

Los comentarios en Rust se agregan usando una doble diagonal (//):

// This is a comment
Enter fullscreen mode Exit fullscreen mode

Variables

Para definir una variable se usa la palabra reservada let seguida del nombre de la variable y el valor asignado.

let name = expressions;
Enter fullscreen mode Exit fullscreen mode

Ejemplo

let a = 7;
let b = 8;
println!(La suma es: {}, a+b);
Enter fullscreen mode Exit fullscreen mode

El ejemplo anterior es de la suma de dos números, a y b, cuyo resultado se muestra en pantalla.

Cuando se define una variable, Rust recomienda usarla, en caso contrario quitar esa línea del código. Por ejemplo:

let a = 7;
let b = 8;
let c = a+b;
println(La suma es {}, a+b);
Enter fullscreen mode Exit fullscreen mode

Se modifica el ejemplo anterior y se define una variable c que almacena la suma de a y b. Sin embargo, al no pasarla como parametro a println, nos muestra la siguiente advertencia al ejecutar el programa:

warning: unused variable: `c`                                                   
 --> src/main.rs:4:9                                                            
  |                                                                             
4 |     let c = a+b;                                                            
  |         ^ help: consider using `_c` instead                                 
  |                                                                             
  = note: #[warn(unused_variables)] on by default
Enter fullscreen mode Exit fullscreen mode

Rust también recomienda que si el nombre de una variable lleva más de dos palabras, se debe usar guión bajo (_) para separarlas. Por ejemplo:

let a = 7;
let b = 8;
let suma_ab = a+b;
Enter fullscreen mode Exit fullscreen mode

Constantes

Una constante se define usando la palabra reservada const seguida del nombre en mayúsculas, el tipo de dato y el valor asignado.

const NAME: type = expression;
Enter fullscreen mode Exit fullscreen mode

Ejemplo

const A: i32 = 7;
const B: i32 = 8;
print!("The sum is: {}", A + B);
Enter fullscreen mode Exit fullscreen mode

Mutabilidad

La mutabilidad es una característica de las variables en Rust, por la que su valor sólo puede modificarse cuando se indica, en caso contrario esta permanece inmutable.

Ejemplo

let x = 5;
println!("El valor de x es: {}", x);
x = 6;
println!("El valor de x is: {}", x);
Enter fullscreen mode Exit fullscreen mode

En otros lenguajes de programación, si se desea modificar el valor de una variable, se asigna el nuevo valor como se muestra en el código anterior. En Rust cuando ejecutamos el programa, muestra el siguiente error:

error[E0384]: cannot assign twice to immutable variable `x`                     
 --> src/main.rs:4:5                                                            
  |                                                                             
2 |     let x = 5;                                                              
  |         - first assignment to `x`                                           
3 |     println!("The value of x is: {}", x);                                   
4 |     x = 6;                                                                  
  |     ^^^^^ cannot assign twice to immutable variable
Enter fullscreen mode Exit fullscreen mode

Indica que no se puede asignar valor dos veces a una variable inmutable. Si se necesita modificar su valor, se tiene que indicar que la variable será mutable, para ello se usa la palabra reservada mut.

let mut name = expression;
Enter fullscreen mode Exit fullscreen mode

Ejemplo

let mut x = 5;
println!("The value of x is: {}", x);
x = 6;
println!("The value of x is: {}", x);
Enter fullscreen mode Exit fullscreen mode

Tipos de datos

Los tipos de datos soportados en Rust son los siguientes:

  • i8, i16, i32, i64
  • u8, u16, u32, u64
  • isize, usize
  • f32, f64

Enteros (i) de 8 a 64 bits, enteros sin signo (u) de 8 a 64 bits, isize y usize dependiendo de la arquitectura donde se ejecute el programa, flotantes (f) de 32 y 64 bits.

Ejemplo

let x = 42;
let y = 1.0;
Enter fullscreen mode Exit fullscreen mode

Un valor entero es de tipo i32 por defecto y para tipo flotante es f64 el tipo de dato por defecto.

Operaciones númericas

Los operadores soportados por Rust para operaciones de suma, resta, multiplicación, división y módulo son los mismos que en otros lenguajes.

Ejemplo

let suma = 5 + 10;
let resta = 95.5 - 4.3;
let multiplicacion = 4 * 30;
let division = 56.7 / 32.2;
let modulo = 43 % 5;
Enter fullscreen mode Exit fullscreen mode

Caracteres

Las variables de tipo char en Rust tienen la siguiente sintaxis:

let x: char = x;
Enter fullscreen mode Exit fullscreen mode

Booleanos

Las variables de tipo booleano pueden tomar los valores de true, false y se definen como sigue:

let x = true;
let y: bool = false;
Enter fullscreen mode Exit fullscreen mode

Sentencias condicionales

En Rust se usa if y else para evaluar condiciones y ejecutar bloques de código si una condición se cumple o no.

if condition {
    expressions;
}
else {
    expressions;
}
Enter fullscreen mode Exit fullscreen mode

Ejemplo
Se tienen dos variables a y b, se necesita determinar cual de estas tiene mayor valor.

let a = 8;
let b = 12;
if a > b {
    println!("{} > {}", a, b);
}
else {
    println!("{} > {}", b, a);
}
Enter fullscreen mode Exit fullscreen mode

Para evaluar una segunda o más condiciones se usa else if.

Ejemplo
Determinar si un número es divisible por 4, 3 o 2, o no es divisible por 4.

let number = 6;
if number % 4 == 0 {
    println!("number is divisible by 4");
} else if number % 3 == 0 {
    println!("number is divisible by 3");
} else if number % 2 == 0 {
    println!("number is divisible by 2");
} else {
    println!("number is not divisible by 4, 3, or 2");
}
Enter fullscreen mode Exit fullscreen mode

Tuplas

Una de las estructuras de datos soportadas por Rust son las tuplas. Se define usando la palabra reservada let, seguido del nombre, los tipos de datos (opcional) de cada valor y los valores a almacenar entre paréntesis. Una tupla tiene un tamaño fijo que no puede cambiar una vez definida.

Ejemplo:

let tup: (i32, f64, u8) = (500, 6.4, 1);
Enter fullscreen mode Exit fullscreen mode

Arreglos

Para definir un arreglo en Rust se usa la palabra reservada let, seguido del nombre, el tipo de dato y tamaño (opcional), y los valores entre corchetes. Un arreglo puede ser mutable (mut).

Ejemplo:

let a = [1, 2, 3];
let mut m = [1, 2, 3];
Enter fullscreen mode Exit fullscreen mode

Ciclos

Se dispone de los ciclos loop, for y while. El primero se trata de un ciclo infinito, for y while que se conocen de otros lenguajes de programación, y cuya sintaxis se muestra en seguida.

loop
Ciclo infinito que solo termina cuando se detiene la ejecución del programa.

loop {
    expressions;
}
Enter fullscreen mode Exit fullscreen mode

Ejemplo

loop {
    println!(“¡Otra vez!);
}
Enter fullscreen mode Exit fullscreen mode

for
La sintaxis general del ciclo for es la que se muestra a continuación. Se usa una variable para delimitar el número de veces que el bloque de código dentro del ciclo se ejecutará. Esta variable no tiene que ser definida previamente y su valor inicial y el número de veces que se repetirá, se separan por dos puntos (..)..

for i in x..n {
    expressions;
}
Enter fullscreen mode Exit fullscreen mode

Ejemplo
Imprimir los elementos de un arreglo usando un ciclo for.

let a = [1, 2, 3, 4, 5];
for i in 0..5 {
    println!({}, a[i]);
}
Enter fullscreen mode Exit fullscreen mode

Si no se conoce el tamaño de un arreglo se puede usar la función iter().

let a = [1, 2, 3, 4, 5];
for i in a.iter() {
    println!({}, i);
}
Enter fullscreen mode Exit fullscreen mode

Usando la función se recorre cada posición dentro del arreglo y los valores se asignan a la variable i.

Si se requiere imprimir los elementos del arreglo en orden inverso, se puede usar la función rev().

let a = [1, 2, 3, 4, 5];
for n in (0..5).rev() {
    println!("{}", a[n]);
}
Enter fullscreen mode Exit fullscreen mode

El bloque de código anterior imprime los elementos del arreglo de la última posición a la primera.

while
La sintaxis del ciclo while es similar a la que se conoce de otros lenguajes, definimos una variable que delimitará el número de veces que el bloque de código si la condición se cumple. La sintaxis en Rust es como sigue:

while condition {
    expressions;
}
Enter fullscreen mode Exit fullscreen mode

Ejemplo
Imprimir el contenido de un arreglo usando un ciclo while.

let a = [1, 2, 3, 4, 5];
let mut i = 0;
while i < 5 {
    println!({}, a[i]);
    i = i + 1;
}
Enter fullscreen mode Exit fullscreen mode

Funciones

Una función en Rust se define usando la palabra reservada fn, seguida del nombre de la función, paréntesis, y si esta recibe parámetros, se coloca dentro de los paréntesis el nombre de la variable que recibirá el valor y el tipo de dato. Si la función devuelve algún valor, se debe especificar el tipo de dato que devolverá.

fn name(arg: Type) -> ReturnType {
    statements;
}
Enter fullscreen mode Exit fullscreen mode

Ejemplo
Define una función que devuelva la suma de los valores de las variables a y b.

fn main() {
    let a = 9;
    let b = 15;
    suma(a, b);
}

fn suma(a: i32, b: i32) {
    let c = a + b;
    println!(La suma de {} + {} es: {}, a, b, c);
}
Enter fullscreen mode Exit fullscreen mode

La definición de una función puede colocarse antes o después de la función principal, no afectando esto la ejecución del programa. El nombre de la función, si contiene dos palabras, estas deben separarse con un guión bajo (_).

Ejemplo

fn cinco() -> i32 {
    5
}
fn main() {
    let x = cinco();
    println!(El valor de x es {}, x);
}
Enter fullscreen mode Exit fullscreen mode

En el ejemplo anterior, si una función devuelve un valor o una variable, puede o no usarse la palabra reservada return, y colocarse el valor que devolverá sin punto y coma (;) al final.

Esta fue una breve introducción al lenguaje de programación Rust, para ampliar la información descrita en las líneas anteriores, el sitio oficial es un buen punto de partida, ahí se encontrarán enlaces a la documentación oficial y recursos útiles.

💖 💪 🙅 🚩
mattdark
Mario García

Posted on January 17, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

December Surely Looks Busy!
opensource December Surely Looks Busy!

November 29, 2024

December Surely Looks Busy!
opensource December Surely Looks Busy!

November 29, 2024

Daemons on macOS with Rust
undefined Daemons on macOS with Rust

November 29, 2024