Programación asíncrona en Rust: guía práctica completa

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción a la programación asíncrona en Rust



Las aplicaciones modernas requieren cada vez más un alto rendimiento al trabajar con redes, sistemas de archivos o bases de datos. La programación asíncrona en Rust permite manejar eficientemente miles de conexiones simultáneas sin crear un hilo por cada tarea. A diferencia del multihilo, el código asíncrono utiliza menos memoria y escala mejor.



Rust ofrece un enfoque único para la asincronía, basado en abstracciones de costo cero. Esto significa que solo pagas por lo que usas, sin sobrecarga por funcionalidades no utilizadas. En esta guía, desglosaremos los conceptos clave y escribiremos ejemplos prácticos que te ayudarán a empezar a escribir código asíncrono en Rust hoy mismo.



Fundamentos de async/await y Futures



¿Qué es Future?



Future es un trait que representa una operación asíncrona que puede completarse en el futuro. En Rust, Future es una entidad perezosa: no hace nada hasta que se le consulta (poll). La idea principal: en lugar de bloquear un hilo, registramos una tarea y obtenemos el resultado cuando esté listo.



use std::future::Future;use std::pin::Pin;use std::task::{Context, Poll};

struct MyFuture { ready: bool,}

impl Future for MyFuture { type Output = u32;

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<u32> { if self.ready { Poll::Ready(42) } else { // Registramos un waker para una nueva consulta cx.waker().wake_by_ref(); Poll::Pending } }}


async/await — azúcar sintáctico



La palabra clave async convierte una función o bloque en un Future. await pausa la ejecución hasta que el Future se complete, devolviendo el control al runtime.



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

async fn fetch_data() -> String { // Simulación de una solicitud de red sleep(Duration::from_secs(2)).await; String::from("Datos recibidos")}

#[tokio::main]async fn main() { let result = fetch_data().await; println!("{}", result);}


Ciclos de vida y borrow checker



Una de las características de Rust es el estricto control de préstamos. En el código asíncrono, esto es especialmente importante, ya que un Future puede vivir más que los datos prestados. Usa async move para tomar posesión:



async fn process_data(data: Vec<u32>) -> u32 {    data.iter().sum()}

#[tokio::main]async fn main() { let numbers = vec![1, 2, 3, 4, 5]; let sum = process_data(numbers).await; println!("Suma: {}", sum);}


Trabajo práctico con Tokio



Instalación y configuración



Tokio es el runtime asíncrono más popular para Rust. Agrega a Cargo.toml:



[dependencies]tokio = { version = "1", features = ["full"] }


Creación de un servidor TCP asíncrono



Consideremos un servidor echo simple que acepta conexiones y envía de vuelta los datos recibidos:



use tokio::net::TcpListener;use tokio::io::{AsyncReadExt, AsyncWriteExt};

#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { let listener = TcpListener::bind("127.0.0.1:8080").await?; println!("Servidor iniciado en el puerto 8080");

loop { let (mut socket, addr) = listener.accept().await?; println!("Nueva conexión desde: {}", addr);

tokio::spawn(async move { let mut buf = [0; 1024]; loop { let n = socket.read(&mut buf).await.unwrap(); if n == 0 { break; } // Conexión cerrada socket.write_all(&buf[..n]).await.unwra

Blogs

Book Recommendations