Rustlings Web
Contenido
  • Introduccion
  • Porque Rust
  • Versiones
  • Configuracion Entorno
  • Sintaxis Basica
  • Tipos Datos
  • Estructuras Control
  • Ownership
  • Borrowing
  • Structs
  • Enums Pattern Matching
  • Temas Avanzados Roadmap
Contenido

Temas Avanzados: Completando el Roadmap de Rust

Este capítulo final cubre los temas restantes del roadmap, proporcionándote una visión general de las características avanzadas de Rust que deberías explorar para convertirte en un desarrollador competente.

Gestión de Errores Avanzada

Propagación de Errores con ?

use std::fs::File;
use std::io::{self, Read};

fn leer_archivo(nombre: &str) -> Result<String, io::Error> {
    let mut archivo = File::open(nombre)?; // ? propaga el error automáticamente
    let mut contenido = String::new();
    archivo.read_to_string(&mut contenido)?;
    Ok(contenido)
}

// Equivalente sin ?:
fn leer_archivo_verboso(nombre: &str) -> Result<String, io::Error> {
    let mut archivo = match File::open(nombre) {
        Ok(archivo) => archivo,
        Err(e) => return Err(e),
    };
    let mut contenido = String::new();
    match archivo.read_to_string(&mut contenido) {
        Ok(_) => Ok(contenido),
        Err(e) => Err(e),
    }
}

Errores Personalizados

use std::fmt;
use std::error::Error;

#[derive(Debug)]
enum MiError {
    Io(std::io::Error),
    Parse(std::num::ParseIntError),
    Custom(String),
}

impl fmt::Display for MiError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            MiError::Io(err) => write!(f, "Error de IO: {}", err),
            MiError::Parse(err) => write!(f, "Error de parseo: {}", err),
            MiError::Custom(msg) => write!(f, "Error personalizado: {}", msg),
        }
    }
}

impl Error for MiError {}

// Conversiones automáticas
impl From<std::io::Error> for MiError {
    fn from(err: std::io::Error) -> MiError {
        MiError::Io(err)
    }
}

impl From<std::num::ParseIntError> for MiError {
    fn from(err: std::num::ParseIntError) -> MiError {
        MiError::Parse(err)
    }
}

Colecciones Esenciales

Vector

fn main() {
    let mut v: Vec<i32> = Vec::new();
    v.push(1);
    v.push(2);
    v.push(3);
    
    // Acceso seguro
    match v.get(2) {
        Some(third) => println!("Tercer elemento: {}", third),
        None => println!("No hay tercer elemento"),
    }
    
    // Iteración
    for i in &v {
        println!("{}", i);
    }
    
    // Modificación durante iteración
    for i in &mut v {
        *i += 50;
    }
}

HashMap

use std::collections::HashMap;

fn main() {
    let mut puntuaciones = HashMap::new();
    puntuaciones.insert(String::from("Azul"), 10);
    puntuaciones.insert(String::from("Amarillo"), 50);
    
    // Actualizar o insertar
    let stat = puntuaciones.entry(String::from("Azul")).or_insert(0);
    *stat += 10;
    
    // Iterar
    for (clave, valor) in &puntuaciones {
        println!("{}: {}", clave, valor);
    }
}

Módulos y Organización del Código

Estructura de Módulos

// src/lib.rs
mod network {
    fn connect() {}
    
    mod client {
        fn connect() {}
    }
    
    mod server {
        fn connect() {}
    }
}

// Usar elementos de módulos
pub use crate::network::client;

// src/network.rs (archivo separado)
pub mod client {
    pub fn connect() {
        println!("Conectando cliente...");
    }
}

pub mod server {
    pub fn connect() {
        println!("Iniciando servidor...");
    }
}

Paths y Use

// Diferentes formas de importar
use std::collections::HashMap;
use std::collections::*;
use std::io::{self, Write};
use std::cmp::Ordering;

// Re-exports
pub use crate::some_module::SomeType;

// Alias
use std::collections::HashMap as Map;

Traits: Funcionalidad Compartida

Definir e Implementar Traits

pub trait Resumen {
    fn resumir(&self) -> String;
    
    // Implementación por defecto
    fn resumir_autor(&self) -> String {
        format!("(Leer más de {}...)", self.resumir())
    }
}

pub struct Noticia {
    pub titular: String,
    pub ubicacion: String,
    pub autor: String,
    pub contenido: String,
}

impl Resumen for Noticia {
    fn resumir(&self) -> String {
        format!("{}, por {} ({})", self.titular, self.autor, self.ubicacion)
    }
}

// Traits como parámetros
pub fn notificar(item: &impl Resumen) {
    println!("¡Breaking news! {}", item.resumir());
}

// Trait bounds
pub fn notificar_detallado<T: Resumen + std::fmt::Display>(item: &T) {
    println!("Detalle: {}", item);
}

Traits Derivables

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct Persona {
    nombre: String,
    edad: u32,
}

fn main() {
    let persona1 = Persona {
        nombre: "Alice".to_string(),
        edad: 30,
    };
    
    let persona2 = persona1.clone(); // Clone
    println!("{:?}", persona1); // Debug
    println!("{}", persona1 == persona2); // PartialEq
}

Lifetimes: Gestión de Referencias

Lifetimes Básicos

// Función con lifetime annotation
fn mas_largo<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

// Struct con lifetimes
struct ImportanteExcerpt<'a> {
    parte: &'a str,
}

impl<'a> ImportanteExcerpt<'a> {
    fn nivel(&self) -> i32 {
        3
    }
    
    fn anunciar_y_retornar_parte(&self, anuncio: &str) -> &str {
        println!("¡Atención por favor: {}!", anuncio);
        self.parte
    }
}

Concurrencia

Hilos Básicos

use std::thread;
use std::time::Duration;

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..10 {
            println!("hola número {} desde el hilo spawned!", i);
            thread::sleep(Duration::from_millis(1));
        }
    });
    
    for i in 1..5 {
        println!("hola número {} desde el hilo principal!", i);
        thread::sleep(Duration::from_millis(1));
    }
    
    handle.join().unwrap();
}

Canales para Comunicación

use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();
    
    thread::spawn(move || {
        let vals = vec![
            String::from("hola"),
            String::from("desde"),
            String::from("el"),
            String::from("hilo"),
        ];
        
        for val in vals {
            tx.send(val).unwrap();
        }
    });
    
    for received in rx {
        println!("Recibido: {}", received);
    }
}

Mutex para Estado Compartido

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];
    
    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }
    
    for handle in handles {
        handle.join().unwrap();
    }
    
    println!("Resultado: {}", *counter.lock().unwrap());
}

Testing

Tests Unitarios

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_suma() {
        assert_eq!(2 + 2, 4);
    }
    
    #[test]
    fn test_panic() {
        panic!("¡Este test fallará!");
    }
    
    #[test]
    #[should_panic(expected = "dividir por cero")]
    fn test_division_por_cero() {
        divide_por_cero();
    }
    
    #[test]
    fn test_result() -> Result<(), String> {
        if 2 + 2 == 4 {
            Ok(())
        } else {
            Err(String::from("dos más dos no es igual a cuatro"))
        }
    }
    
    #[test]
    #[ignore]
    fn test_costoso() {
        // Este test tarda mucho tiempo
    }
}

fn divide_por_cero() {
    let _resultado = 10 / 0;
}

Ecosistema y Librerías Populares

Serialización con Serde

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct Persona {
    nombre: String,
    edad: u8,
    telefono: Vec<String>,
}

fn main() -> serde_json::Result<()> {
    let p = Persona {
        nombre: "John".to_string(),
        edad: 30,
        telefono: vec!["555-1234".to_string()],
    };
    
    let j = serde_json::to_string(&p)?;
    println!("JSON: {}", j);
    
    let p2: Persona = serde_json::from_str(&j)?;
    println!("Deserializado: {:?}", p2);
    
    Ok(())
}

Cliente HTTP con Reqwest

[dependencies]
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1", features = ["full"] }
use reqwest;
use serde::Deserialize;

#[derive(Deserialize, Debug)]
struct Post {
    #[serde(rename = "userId")]
    user_id: u32,
    id: u32,
    title: String,
    body: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let url = "https://jsonplaceholder.typicode.com/posts/1";
    let post: Post = reqwest::get(url)
        .await?
        .json()
        .await?;
    
    println!("{:#?}", post);
    Ok(())
}

CLI con Clap

[dependencies]
clap = { version = "4.0", features = ["derive"] }
use clap::{Arg, Command, Parser};

#[derive(Parser)]
#[command(name = "mi_app")]
#[command(about = "Una aplicación CLI de ejemplo")]
struct Cli {
    #[arg(short, long)]
    nombre: String,
    
    #[arg(short, long, default_value_t = 1)]
    count: u8,
    
    #[arg(short, long)]
    verbose: bool,
}

fn main() {
    let cli = Cli::parse();
    
    for _ in 0..cli.count {
        if cli.verbose {
            println!("¡Hola {} (modo verboso)!", cli.nombre);
        } else {
            println!("¡Hola {}!", cli.nombre);
        }
    }
}

Programación Asíncrona

Async/Await Básico

use tokio::time::{sleep, Duration};

async fn decir_hola() {
    println!("Hola");
    sleep(Duration::from_secs(1)).await;
    println!("Mundo");
}

#[tokio::main]
async fn main() {
    decir_hola().await;
    
    // Ejecutar múltiples tareas concurrentemente
    let task1 = tokio::spawn(async {
        for i in 0..5 {
            println!("Tarea 1: {}", i);
            sleep(Duration::from_millis(100)).await;
        }
    });
    
    let task2 = tokio::spawn(async {
        for i in 0..5 {
            println!("Tarea 2: {}", i);
            sleep(Duration::from_millis(150)).await;
        }
    });
    
    // Esperar a que ambas tareas terminen
    let _ = tokio::join!(task1, task2);
}

Macros Básicos

Macros Declarativos

macro_rules! vec {
    ( $( $x:expr ),* ) => {
        {
            let mut temp_vec = Vec::new();
            $(
                temp_vec.push($x);
            )*
            temp_vec
        }
    };
}

// Macro personalizado
macro_rules! saludar {
    ($nombre:expr) => {
        println!("¡Hola, {}!", $nombre);
    };
    ($nombre:expr, $apellido:expr) => {
        println!("¡Hola, {} {}!", $nombre, $apellido);
    };
}

fn main() {
    saludar!("Juan");
    saludar!("María", "García");
}

Unsafe Rust (Conceptos Básicos)

fn main() {
    let mut num = 5;
    
    // Crear punteros crudos
    let r1 = &num as *const i32;
    let r2 = &mut num as *mut i32;
    
    // Desreferenciar punteros crudos requiere unsafe
    unsafe {
        println!("r1 apunta a: {}", *r1);
        println!("r2 apunta a: {}", *r2);
    }
    
    // Función unsafe
    unsafe fn peligroso() {}
    
    unsafe {
        peligroso();
    }
}

Próximos Pasos en tu Viaje con Rust

Recursos para Continuar Aprendiendo

  1. Libros:

    • The Rust Programming Language (El Libro oficial)
    • Programming Rust (O’Reilly)
    • Rust in Action (Manning)
  2. Práctica:

    • Rustlings: Ejercicios interactivos
    • Advent of Code con Rust
    • Contribuir a proyectos open source
  3. Comunidad:

    • Forum oficial: users.rust-lang.org
    • Discord: discord.gg/rust-lang
    • Reddit: r/rust
    • This Week in Rust (newsletter)
  4. Especialización:

    • Web Development: Actix-web, Warp, Rocket
    • GUI: egui, Tauri, gtk-rs
    • Game Development: Bevy, ggez
    • Systems Programming: kernel modules, drivers
    • WebAssembly: wasm-bindgen, wasm-pack
    • Blockchain: Substrate, Solana
    • Machine Learning: Candle, tch

Proyectos para Practicar

  1. Principiante:

    • Calculadora de línea de comandos
    • Juego de adivinanzas
    • Conversor de unidades
    • To-do list simple
  2. Intermedio:

    • Cliente/servidor HTTP básico
    • Base de datos en memoria
    • Intérprete de expresiones matemáticas
    • Sistema de archivos simple
  3. Avanzado:

    • Compilador para un lenguaje simple
    • Motor de base de datos
    • Sistema operativo mínimo
    • Blockchain básica

Conclusión

¡Felicidades! Has completado un viaje exhaustivo a través del mundo de Rust. Desde los conceptos fundamentales como ownership y borrowing, hasta temas avanzados como concurrencia y el ecosistema de librerías, ahora tienes las bases sólidas para construir aplicaciones robustas y eficientes.

Lo que has aprendido:

✅ Fundamentos sólidos: Sintaxis, tipos de datos, control de flujo
✅ Conceptos únicos de Rust: Ownership, borrowing, lifetimes
✅ Estructuración de código: Structs, enums, módulos
✅ Manejo de errores robusto: Result, Option, propagación
✅ Colecciones y herramientas: Vec, HashMap, iteradores
✅ Funcionalidad compartida: Traits y genericidad
✅ Programación concurrente: Hilos, canales, Mutex
✅ Testing y calidad: Tests unitarios, documentación
✅ Ecosistema rico: Librerías populares y herramientas

El camino por delante:

Rust es un lenguaje que recompensa la paciencia y el estudio cuidadoso. Sus conceptos únicos pueden ser desafiantes al principio, pero proporcionan una base sólida para escribir código seguro, eficiente y mantenible.

Consejos finales:

  1. Práctica constante: La mejor manera de dominar Rust es escribir código
  2. Lee código de otros: Explora proyectos open source en GitHub
  3. Participa en la comunidad: Haz preguntas, ayuda a otros
  4. Mantente actualizado: Rust evoluciona constantemente
  5. Especialízate: Elige un área que te interese y profundiza

¡Ahora es momento de construir cosas increíbles con Rust! 🦀

El lenguaje que garantiza seguridad de memoria, ofrece rendimiento de nivel de sistema, y tiene uno de los ecosistemas más amigables del mundo del desarrollo de software te está esperando.

¡Bienvenido a la comunidad Rust!


Anterior Enums Pattern Matching
Siguiente -
Editor

Output

$ cargo run