Durante años Rust se posicionó como lenguaje para “systems programming” — sistemas operativos, drivers, motores de base de datos. En 2023 esa percepción ya está desfasada. Con tokio como runtime asíncrono y axum como framework HTTP, escribir servicios web en Rust es razonablemente cómodo y la diferencia de rendimiento frente a Go o Node es real.
El stack actual
Cuando se habla de “backend en Rust” en 2023, casi siempre se refiere a esta combinación:
- tokio: el runtime async dominante. Maneja un número alto de tareas concurrentes con un número pequeño de hilos del SO usando un scheduler work-stealing.
- axum: framework HTTP construido sobre tower y hyper. Mantenido por el equipo de tokio.
- serde: el estándar de facto para serializar/deserializar JSON, YAML, TOML, etc.
- sqlx o diesel para acceso a base de datos — sqlx con verificación en tiempo de compilación de queries SQL es la opción que más crece.
Combinado, este stack permite construir un microservicio JSON sobre Postgres en cantidad similar de código a un equivalente Go, con mejor seguridad de tipos y errores más informativos en compilación.
Un endpoint mínimo en axum
use axum::{routing::get, Router, Json};
use serde::Serialize;
#[derive(Serialize)]
struct Health { status: &'static str }
async fn health() -> Json<Health> {
Json(Health { status: "ok" })
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/health", get(health));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
Tres conceptos importantes:
#[tokio::main]arranca el runtime async para que elmainsea una funciónasync.- Los handlers son funciones
asyncnormales — axum se encarga del binding entre tipos y respuestas HTTP. Json<T>es un extractor: si lo pones como tipo de retorno, axum sabe serializar a JSON con el header correcto.
Por qué la curva de aprendizaje pesa
No hay que minimizarlo: Rust tiene curva. Para alguien que viene de Go o TypeScript, los primeros meses cuestan. Los puntos de fricción más comunes:
- Ownership y lifetimes. Rust no recolecta basura — el compilador valida que cada referencia sea válida. Conceptos nuevos para la mayoría.
- Async en Rust. Los
Futureson lazy y dependen del runtime. Errores de compilación con tiposPin<Box<dyn Future...>>desconciertan al principio. - Errores explícitos. No hay excepciones. Cada función fallible devuelve
Result<T, E>y se propaga con?. Cambia mentalidad. - Compilación lenta. Un proyecto medio compila en 30-60 segundos en debug, varios minutos en release. Ralentiza el ciclo iterativo.
Para superarlo conviene aceptar 2-3 meses de productividad reducida antes de empezar a sentirse cómodo. Equipos sin esa paciencia abandonan a las dos semanas.
Comparativa pragmática
| Aspecto | Rust + axum | Go + net/http | Node + Express |
|---|---|---|---|
| Latencia p99 baja | Excelente | Muy buena | Aceptable |
| Uso de memoria | Muy bajo | Bajo | Medio-alto |
| Curva aprendizaje | Empinada | Suave | Fácil |
| Velocidad de compilación | Lenta | Rápida | N/A (interpretado) |
| Ecosistema HTTP | Maduro y creciendo | Muy maduro | Amplísimo |
| Productividad para CRUD | Media | Alta | Muy alta |
Rust gana donde el rendimiento por nodo importa de verdad: servicios bajo carga sostenida, consumo de mensajes de Kafka a tasas altas, gateways que multiplexan miles de conexiones. En servicios CRUD típicos donde la lentitud es la base de datos, no el lenguaje, la ganancia es marginal y el coste de aprendizaje no compensa.
Casos donde compensa
En proyectos reales he visto Rust adoptarse con éxito en:
- Gateways y proxies. Manejar 50.000 conexiones simultáneas con uso de memoria predecible.
- Procesadores de eventos. Kafka consumers con throughput alto donde Go ya empezaba a notar GC.
- Servicios CPU-bound. Procesamiento de imágenes, parsing de logs voluminosos, transformaciones complejas.
- Componentes embedidos en otros lenguajes. Compilar a una librería estática consumida desde Python o Node vía FFI.
Para el típico microservicio de “validar JSON, hacer 3 queries a Postgres, devolver respuesta”, Go o Node siguen siendo opciones más productivas.
Patrones que ahorran sufrimiento
Si decides adoptar Rust en backend, algunos consejos prácticos:
anyhowpara errores en aplicación,thiserrorpara errores de librería. Hace la propagación de errores razonable.tracingpara logs estructurados. Funciona bien con OpenTelemetry y se integra con axum.towermiddleware. Composición uniforme — logging, autenticación, rate limiting reutilizables.- Build incremental con
moldcomo linker. Reduce tiempos de iteración significativamente en Linux. cargo nextest. Un test runner mucho más rápido quecargo test.
Conclusión
Rust en backend es ya una opción seria en 2023, no una promesa futura. La combinación tokio + axum es estable, performante y razonablemente productiva una vez superada la curva inicial. Pero no es la respuesta correcta para cualquier servicio — es una elección con trade-offs claros que conviene hacer conscientemente, basándose en cargas de trabajo reales y no en hype.
Síguenos en jacar.es para más sobre arquitectura backend y elección de stack tecnológico.