Rust

Rust

Tutorial Rust: Primer programa

Aprende a crear y ejecutar tu primer programa en Rust con Cargo, gestionando proyectos y compilación paso a paso.

Aprende Rust y certifícate

Estructura Cargo

Cuando trabajamos con Rust, utilizamos Cargo como el sistema de gestión de paquetes y herramienta de construcción oficial. Cargo se encarga de muchas tareas esenciales como descargar dependencias, compilar el código y generar ejecutables.

Al crear un nuevo proyecto con Cargo, se genera automáticamente una estructura de directorios específica que sigue convenciones estándar. Esta organización facilita el desarrollo y mantenimiento de proyectos Rust.

Para crear un nuevo proyecto, abrimos una terminal y ejecutamos:

cargo new mi_proyecto

Este comando genera un nuevo directorio llamado mi_proyecto con la siguiente estructura:

mi_proyecto/
├── Cargo.toml
└── src/
    └── main.rs

Vamos a examinar cada elemento de esta estructura:

Directorio raíz del proyecto

El directorio raíz (en este caso mi_proyecto) contiene todos los archivos y carpetas relacionados con nuestro proyecto. Este es el espacio de trabajo principal donde Cargo buscará la configuración y los archivos fuente.

Archivo Cargo.toml

El archivo Cargo.toml es el manifiesto del proyecto y contiene la configuración y metadatos necesarios. TOML (Tom's Obvious, Minimal Language) es un formato de configuración simple y legible. Veamos un ejemplo básico:

[package]
name = "mi_proyecto"
version = "0.1.0"
edition = "2021"

[dependencies]

Las secciones principales son:

  • [package]: Contiene la información básica del proyecto:

  • name: El nombre del proyecto

  • version: La versión actual siguiendo el formato de versionado semántico

  • edition: La edición de Rust que se utilizará (2015, 2018 o 2021)

  • [dependencies]: Aquí se declaran las bibliotecas externas (crates) que nuestro proyecto utilizará. Inicialmente está vacía.

Directorio src/

El directorio src/ (source) es donde se almacena todo el código fuente de nuestro proyecto. Cargo espera encontrar aquí los archivos .rs que contienen nuestro código Rust.

Archivo main.rs

Para un proyecto ejecutable (en contraposición a una biblioteca), Cargo crea automáticamente el archivo src/main.rs. Este archivo contiene el punto de entrada de nuestro programa, la función main().

Otros archivos generados

Cuando compilamos nuestro proyecto, Cargo genera automáticamente algunos archivos y directorios adicionales:

  • target/: Directorio donde se almacenan los resultados de la compilación. Contiene:

  • Archivos objeto intermedios

  • Ejecutables finales

  • Documentación generada

  • Cargo.lock: Este archivo se genera automáticamente después de la primera compilación y registra las versiones exactas de todas las dependencias. Esto garantiza compilaciones reproducibles.

Estructura para proyectos más complejos

A medida que nuestro proyecto crece, podemos añadir más elementos a esta estructura básica:

  • tests/: Directorio para pruebas de integración
  • examples/: Ejemplos de uso de nuestra biblioteca
  • benches/: Pruebas de rendimiento
  • src/bin/: Múltiples ejecutables dentro del mismo proyecto

Para proyectos iniciales, la estructura básica es suficiente y nos permite concentrarnos en aprender el lenguaje sin preocuparnos por la organización de archivos.

Convenciones de nombres

Cargo sigue algunas convenciones importantes:

  • Los nombres de proyectos usan guiones (mi-proyecto) en Cargo.toml
  • Los nombres de paquetes en el código Rust usan guiones bajos (mi_proyecto)
  • Los archivos de código usan snake_case (mi_archivo.rs)

Estas convenciones ayudan a mantener la consistencia entre proyectos Rust y facilitan la colaboración entre desarrolladores.

main.rs básico

Ahora que entendemos la estructura de un proyecto Rust, vamos a examinar el archivo main.rs que Cargo ha generado automáticamente. Este archivo contiene el código inicial de nuestro programa y es el punto de partida para cualquier aplicación Rust.

Al abrir el archivo src/main.rs con tu editor de código favorito, encontrarás el siguiente contenido:

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

Aunque parece simple, este pequeño programa contiene varios elementos fundamentales de Rust que debemos entender:

Función main

La línea fn main() { define la función principal de nuestro programa:

fn main() {
    // Código de la función
}
  • fn es la palabra clave que indica que estamos definiendo una función
  • main es el nombre de la función - esta función específica es especial
  • () indica que la función no recibe parámetros
  • { } son las llaves que delimitan el cuerpo de la función

La función main es el punto de entrada de cualquier programa ejecutable en Rust. Cuando ejecutamos nuestro programa, el sistema operativo comienza la ejecución desde esta función. Sin una función main, no tendríamos un programa ejecutable.

Instrucción println!

Dentro de la función main, encontramos esta línea:

println!("Hello, world!");

Esta instrucción muestra texto en la consola:

  • println! es una macro de Rust (no una función normal)
  • Las macros en Rust se identifican por el signo de exclamación !
  • "Hello, world!" es una cadena de texto literal (string)
  • La instrucción termina con ; (punto y coma), que marca el final de la expresión

¿Qué es una macro?

Las macros en Rust son diferentes de las funciones normales. Una macro es un tipo de código que escribe otro código (metaprogramación). No necesitas entender completamente las macros ahora, pero es importante saber que println! es una macro que facilita la impresión de texto formateado en la consola.

Modificando nuestro primer programa

Podemos modificar este programa básico para que muestre un mensaje personalizado. Por ejemplo:

fn main() {
    println!("¡Mi primer programa en Rust!");
}

También podemos añadir más llamadas a println! para mostrar múltiples líneas:

fn main() {
    println!("¡Mi primer programa en Rust!");
    println!("Estoy aprendiendo un nuevo lenguaje.");
}

Imprimiendo valores variables

La macro println! puede hacer más que simplemente imprimir texto estático. También puede formatear e imprimir valores:

fn main() {
    let lenguaje = "Rust";
    let año = 2023;
    println!("Estoy aprendiendo {} en {}.", lenguaje, año);
}

En este ejemplo:

  • let lenguaje = "Rust"; crea una variable llamada lenguaje con el valor "Rust"
  • let año = 2023; crea otra variable llamada año con el valor numérico 2023
  • Las llaves {} dentro del string son marcadores de posición que serán reemplazados por los valores de las variables

Comentarios en Rust

Es una buena práctica añadir comentarios a tu código para explicar lo que hace. En Rust, los comentarios se escriben así:

fn main() {
    // Esto es un comentario de una línea
    
    /* Esto es un comentario
       que abarca múltiples
       líneas */
       
    println!("¡Hola desde Rust!"); // Los comentarios pueden ir al final de una línea
}

Los comentarios son ignorados por el compilador y sirven como notas para los programadores.

Estructura básica completa

Juntando todo lo que hemos visto, un archivo main.rs básico pero completo podría verse así:

// Mi primer programa en Rust
fn main() {
    // Definimos algunas variables
    let nombre = "Programador";
    let lenguaje = "Rust";
    
    // Imprimimos mensajes en la consola
    println!("¡Hola, {}!", nombre);
    println!("Bienvenido al mundo de {}.", lenguaje);
    println!("Este es tu primer programa.");
}

Este programa muestra tres líneas de texto en la consola, incluyendo valores de variables. Es simple pero ilustra los conceptos básicos de un programa Rust.

Recuerda que cada instrucción en Rust termina con un punto y coma (;), excepto cuando la última expresión de un bloque es el valor de retorno de ese bloque (algo que veremos más adelante).

Build y ejecución

Una vez que hemos creado nuestro proyecto y entendido la estructura básica del archivo main.rs, el siguiente paso es compilar y ejecutar nuestro programa. Cargo nos proporciona varias herramientas para facilitar este proceso.

El ciclo de desarrollo en Rust

El ciclo de desarrollo típico en Rust sigue estos pasos:

  1. Escribir o modificar el código
  2. Comprobar que el código no tiene errores
  3. Compilar el código en un ejecutable
  4. Ejecutar el programa
  5. Repetir

Cargo nos ofrece comandos específicos para cada una de estas etapas, lo que simplifica enormemente nuestro flujo de trabajo.

Verificación del código con cargo check

Antes de compilar completamente nuestro programa, podemos usar cargo check para verificar rápidamente si nuestro código tiene errores:

cargo check

Este comando analiza nuestro código sin generar un ejecutable, lo que lo hace mucho más rápido que una compilación completa. Es ideal para comprobar errores de sintaxis o problemas de tipado mientras estamos escribiendo código.

Si nuestro código no tiene errores, veremos un mensaje como:

Checking mi_proyecto v0.1.0
Finished dev [unoptimized + debuginfo] target(s) in 0.15s

Si hay errores, Cargo nos mostrará mensajes detallados indicando dónde están los problemas y sugerencias para solucionarlos.

Compilación con cargo build

Para compilar nuestro programa y generar un ejecutable, usamos:

cargo build

Este comando compila nuestro código y crea un ejecutable en el directorio target/debug/. Por defecto, Cargo compila en modo de desarrollo, que incluye información de depuración pero no está optimizado para rendimiento.

Si la compilación es exitosa, veremos algo como:

Compiling mi_proyecto v0.1.0
Finished dev [unoptimized + debuginfo] target(s) in 0.72s

El ejecutable generado se encuentra en target/debug/mi_proyecto (o target\debug\mi_proyecto.exe en Windows).

Compilación para producción

Cuando nuestro programa está listo para ser distribuido, podemos compilarlo con optimizaciones usando la bandera --release:

cargo build --release

Este comando genera un ejecutable optimizado en target/release/ que es más rápido pero tarda más en compilarse y no incluye información de depuración:

Compiling mi_proyecto v0.1.0
Finished release [optimized] target(s) in 1.21s

Ejecución del programa

Hay dos formas principales de ejecutar nuestro programa:

  1. Ejecutar directamente el archivo compilado:
# En sistemas Unix/Linux/macOS
./target/debug/mi_proyecto

# En Windows
.\target\debug\mi_proyecto.exe
  1. Usar el comando cargo run:
cargo run

El comando cargo run es especialmente útil porque compila y ejecuta nuestro programa en un solo paso. Si el código ha cambiado desde la última compilación, Cargo lo recompilará automáticamente antes de ejecutarlo.

Si queremos ejecutar la versión optimizada:

cargo run --release

Ejemplo práctico

Veamos un ejemplo completo del flujo de trabajo:

  1. Modificamos nuestro programa en src/main.rs:
fn main() {
    let mensaje = "¡Hola desde Rust!";
    println!("{}", mensaje);
    println!("Este es mi primer programa compilado y ejecutado.");
}
  1. Verificamos que no hay errores:
cargo check
  1. Compilamos y ejecutamos en un solo paso:
cargo run

Veremos una salida similar a esta:

Compiling mi_proyecto v0.1.0
Finished dev [unoptimized + debuginfo] target(s) in 0.42s
Running `target/debug/mi_proyecto`
¡Hola desde Rust!
Este es mi primer programa compilado y ejecutado.

Limpieza del proyecto

Con el tiempo, el directorio target/ puede crecer considerablemente. Para eliminar todos los archivos compilados y empezar desde cero:

cargo clean

Este comando elimina todo el directorio target/, liberando espacio en disco.

Mensajes de error

Una de las características más destacadas de Rust es la calidad de sus mensajes de error. Si cometemos un error en nuestro código, Cargo nos proporcionará información detallada y útil.

Por ejemplo, si olvidamos un punto y coma:

fn main() {
    let mensaje = "¡Hola desde Rust!"  // Falta el punto y coma
    println!("{}", mensaje);
}

Al ejecutar cargo check o cargo build, veremos un mensaje de error claro:

error: expected `;`, found `println`
 --> src/main.rs:3:5
  |
2 |     let mensaje = "¡Hola desde Rust!"
  |                                      ^ help: add `;` here
3 |     println!("{}", mensaje);
  |     ^^^^^^^ unexpected token

Estos mensajes no solo indican dónde está el error, sino que también sugieren cómo solucionarlo.

Resumen del flujo de trabajo

Para un desarrollo eficiente en Rust, podemos seguir este flujo de trabajo:

  1. Usar cargo check frecuentemente mientras escribimos código para detectar errores rápidamente
  2. Usar cargo run para probar nuestro programa durante el desarrollo
  3. Usar cargo build --release cuando estemos listos para crear la versión final optimizada

Este ciclo de desarrollo nos permite trabajar de manera eficiente y aprovechar al máximo las herramientas que Cargo nos proporciona.

Aprende Rust online

Otras lecciones de Rust

Accede a todas las lecciones de Rust y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Accede GRATIS a Rust y certifícate

Ejercicios de programación de Rust

Evalúa tus conocimientos de esta lección Primer programa con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

En esta lección

Objetivos de aprendizaje de esta lección

  • Comprender la estructura básica de un proyecto Rust generado por Cargo.
  • Identificar los archivos y directorios principales como Cargo.toml, src/main.rs y target/.
  • Entender la función main y la macro println! para imprimir en consola.
  • Aprender a compilar, verificar y ejecutar programas Rust usando comandos Cargo.
  • Interpretar mensajes de error comunes y aplicar buenas prácticas como el uso de comentarios.