Parte 2 - Rust en tu ordenador

Como se ha visto hasta el momento, Rust se puede aprender utilizando Playground. Pero lo normal, será desarrollar con Rust en un ordenador personal. Además, existen cosas, como utilizar ficheros o crear librerías y programas de más de un fichero, que no se pueden hacer en Playground.

Lo más importante es poder usar todo tipo de librerías (crates) para construir programas completos.

cargo

rustc signifca compilador de Rust, y es el programa que compila el código fuente. Los ficheros de rust se escriben con la extensión .rs. En todo caso, la mayoría de las personas no escriben rustc main.rs para compilar un programa. Normalmente, se usa cargo que es el programa gestor de paquetes de Rust.

Una nota sobre el nombre: se llama cargo porque cuando se unen diversas "crates" (cajas, librerías) se tiene toda una carga de cajas. Un "crate" es una caja como las que se ven en los barcos o en los camiones trailer. Pero todo proyecto Rust también se llama así "crate". Cuando se unen todas las librerías, se consigue una carga completa.

A continuación se prueba cargo para ejecutar un proyecto que utiliza la librería (crate) rand para elegir de forma aleatoria entre ocho letras posibles.

use rand::seq::SliceRandom; // Use this for .choose over slices

fn main() {

    let my_letters = vec!['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];

    let mut rng = rand::thread_rng();
    for _ in 0..6 {
        print!("{} ", my_letters.choose(&mut rng).unwrap());
    }
}

El código anterior imprime algo como b c g h e a. Pero lo que se quiere ver es cómo funciona cargo. Para usarlo y ejecutar este programa, normalmente se usará cargo run. Esto compilará, construirá y ejecutará el programa. Pero cuando se empieza a compilar, se observará algo como esto:

   Compiling getrandom v0.1.14
   Compiling cfg-if v0.1.10
   Compiling ppv-lite86 v0.2.8
   Compiling rand_core v0.5.1
   Compiling rand_chacha v0.2.2
   Compiling rand v0.7.3
   Compiling rust_book v0.1.0 (C:\Users\mithr\OneDrive\Documents\Rust\rust_book)
    Finished dev [unoptimized + debuginfo] target(s) in 13.13s
     Running `C:\Users\mithr\OneDrive\Documents\Rust\rust_book\target\debug\rust_book.exe`
g f c f h b

Se ve que no solo usa rand, sino otras librerías. Esto es debido a que rand también necesita a otras librerías. cargo se encarga de encontrar todas las librerías necesarias y las compila juntas. En este caso solo se necesitan siete librerías, pero en proyectos grande pueden aparecer 200 o más librerías.

Este es un inconveniente de Rust. Es muy rápido, porque se compila por adelantado. Lo hace mirando el código y viendo qué hace. Si se escribe el siguiente código:

use std::fmt::Display;

fn print_and_return_thing<T: Display>(input: T) -> T {
    println!("You gave me {} and now I will give it back.", input);
    input
}

fn main() {
    let my_name = print_and_return_thing("Windy");
    let small_number = print_and_return_thing(9.0);
}

Esta función puede tomar cualquier tipo que tenga el rasgo Display, así que se le pasa un &str y en el siguiente uso un f64 y no falla. Pero el compilador no usa genéricos, porque no deja nada para la posterior ejecución del programa. El compilador construye un programa que pueda ejecutarse lo más rápido posible. Por eso cuando mura a la primer parte con "Windy", genera una función particular como fn print_and_return_thing(input: &str) -> &str. Y cuando se usa un f64, construye, a partir del código genérico, otra función fn print_and_return_thing(input: f64) -> f64. Todo los chequeos sobre los rasgos se hace durante la compilación y esta es la razón por la que se tarda más en compilar el programa, porque necesita analizar el código y construir las versiones concretas de cada código genérico que hay.

Otra cosa más: En 2020 se está trabajando en reducir el tiempo de compilación de Rust, puesto que es lo que más tiempo lleva. Cada versión de Rust es un poco más rápida al compilar y existen diversos planes para acelerarla más. Mientras tanto, esto es lo más importante a conocer:

  • cargo build compila el programa para que se pueda ejecutar.
  • cargo run lo compila y ejecuta.
  • cargo build --release y cargo run --release hace lo mismo que los anteriores, pero optimizado para distribuir. ¿Qué es esto? Es el modo en el que se compila el programa cuando ya se ha terminado. En este modo, Rust tarda aún más en compilar, pero lo hace para que el programa resultante sea lo más rápido posible. El modo "release" es mucho más rápido en ejecución que el modo normal, que se llama modo de depuración. Este otro modo se llama así porque es más rápido en compilar y tiene más información de depuración. El modo cargo build normal se llama "construcción de depuración" (debug build) y cargo build --release se llama construcción para distribución (release build).
  • cargo check sirve para validar el código. Es como compilar, pero sin que genere el código ejecutable. Es una forma de validar si no se quiere ejecutar el código, ya que es más rápida que build o run.

Por cierto, la parte --release de la sentencia de cargo es un flag. Es información extra que recibe la sentencia cargo que sirve para cambiar su comportamiento.

Algunas otras cosas que son necesarias conocer sobre cargo:

  • cargo new sirve para crear un nuevo proyecto de Rust. Después de la palabra new se escribe el nombre del proyecto y cargo creará una carpeta y los ficheros mínimos necesarios para ese proyecto.
  • cargo clean sirve para eliminar las librerías que se hayan descargado por ser necesarias para el proyecto. Estas librerías se habrán ido añadido como filas en el fichero Cargo.toml y se habrán ido descargando como consecuencia de haber compilado el proyecto con cargo build o cargo run.

Una última cosa a conocer sobre el compilador: siempre tarda más la primera vez que se ejecuta cargo build o carga run. Después de esta primera vez, recordará las librerías usadas y compilará más rápido. Eso sí, después de un cargo clean, volverá a tardar mucho en compilar ya que es como si fuese la primera vez que lo hace.