El ecosistema esencial: las crates que todo Rustacean conoce
La biblioteca estándar de Rust es deliberadamente pequeña: no trae serialización, ni runtime async, ni cliente HTTP. Esa ausencia es una decisión de diseño que delega en el ecosistema, donde un puñado de crates fundacionales —serde, tokio, clap, rayon, reqwest, axum— se han vuelto estándares de facto y lenguas francas sobre las que se apoya casi todo lo demás.
Si vienes de lenguajes con «pilas incluidas», la biblioteca estándar de Rust te parecerá austera hasta el desconcierto: no trae serialización JSON, ni un runtime asíncrono, ni un cliente HTTP, ni parseo de argumentos de línea de comandos. No es un olvido: es una postura. La std de Rust promete estabilidad perpetua —lo que entra jamás puede romper—, así que se restringe a lo verdaderamente universal y delega el resto en crates.io, donde las librerías pueden evolucionar, subir mayores y competir. El resultado es que ser productivo en Rust exige conocer un canon: media docena de crates fundacionales que la comunidad ha coronado como estándares de facto. No dominarlas es reinventar mal lo que decenas de miles de proyectos ya comparten. Esta lección es el mapa de ese canon.
- Entender por qué la
stdes pequeña a propósito y qué llena el ecosistema en su lugar. - Reconocer las crates fundacionales —
serde,tokio,clap,rayon— y el problema que resuelve cada una. - Situar el grafo de la web asíncrona:
hyper,tower,axum,reqwestsobretokio. - Identificar las lenguas francas cuyos traits comparten miles de crates entre sí.
Por qué la stdlib es pequeña a propósito
La decisión de fondo es un intercambio entre dos garantías incompatibles. La std ofrece estabilidad absoluta: código escrito hace años sigue compilando, porque la biblioteca estándar nunca publica una versión mayor que rompa. Pero esa misma inmovilidad sería veneno para una API de serialización o un runtime async, dominios donde el conocimiento del arte avanza rápido y a veces hace falta rehacer las abstracciones. Meter serde o tokio en la std los congelaría en su primer diseño para siempre.
Al dejarlos fuera, Rust les concede lo que necesitan: serde y tokio pueden iterar, aprender de sus errores y publicar mayores cuando corresponde, sin arrastrar a la std ni romper la promesa del lenguaje. El precio es que el recién llegado debe saber qué instalar. La std conserva lo eterno —tipos, colecciones, Result, hilos, Iterator— y el ecosistema aporta lo que evoluciona. Conocer el canon es, por tanto, parte de saber Rust.
Las crates fundacionales
Estas son las columnas del ecosistema, las que aparecen en el Cargo.toml de una fracción enorme de todos los proyectos:
serde
El marco de serialización. #[derive(Serialize, Deserialize)] sobre tus tipos, y serde_json, toml o bincode como formatos. Su rasgo genial: separa el modelo de datos del formato concreto.
tokio
El runtime asíncrono dominante. Ejecuta tus Future, programa tareas sobre un pool de hilos y ofrece E/S sin bloqueo, temporizadores y canales. El corazón de casi todo lo async.
clap
El parseo de argumentos de línea de comandos. Con #[derive(Parser)] describes tu CLI como un struct y obtienes ayuda, validación y autocompletado gratis.
rayon
Paralelismo de datos casi mágico: cambia .iter() por .par_iter() y tu bucle se reparte entre todos los núcleos, con la seguridad de datos garantizada por el compilador.
reqwest
El cliente HTTP de alto nivel. Peticiones async (o bloqueantes), JSON con serde integrado, TLS, cookies y redirecciones. Construido sobre hyper y tokio.
axum
El framework web ergonómico del ecosistema tokio. Enrutado por tipos, extractores, y compatible con el middleware de tower. Servidores async idiomáticos.
Un vistazo a serde basta para captar por qué estas crates dominan: la ergonomía es la de la biblioteca estándar, pero el poder es de una librería que pudo evolucionar. Deriva dos traits y tu tipo habla ya con cualquier formato del ecosistema:
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct Config { host: String, puerto: u16 }
let c = Config { host: "localhost".into(), puerto: 8080 };
let json = serde_json::to_string(&c)?; // a JSON
let vuelta: Config = serde_json::from_str(&json)?; // y de vuelta
let toml = toml::to_string(&c)?; // el MISMO tipo, otro formato
El struct no sabe nada de JSON ni de TOML: serde separa el modelo de datos del formato, y esa abstracción es la que convierte un tipo cualquiera en universalmente serializable.
Más allá de esta primera fila hay una segunda que también conviene tener en el radar: anyhow y thiserror para errores (que ya recorriste), tracing para observabilidad estructurada, regex para expresiones regulares, rand para aleatoriedad, itertools para adaptadores extra de iterador, sqlx y diesel para bases de datos, criterion para benchmarks, y proptest para pruebas basadas en propiedades. Ninguna vive en la std, y todas son, en la práctica, tan estándar como si lo estuvieran.
El grafo de la web asíncrona
Las crates no son islas: se apilan. El ejemplo canónico es la pila web de tokio, donde cada capa se construye sobre la anterior y tower aporta un vocabulario común de middleware —Service— que tanto el servidor como el cliente reutilizan:
flowchart TD MIO[mio E/S del sistema epoll o kqueue] --> TOKIO[tokio runtime tareas y temporizadores] TOKIO --> HYPER[hyper implementacion de HTTP] HYPER --> TOWER[tower abstraccion Service y middleware] TOWER --> AXUM[axum servidor web] TOWER --> REQWEST[reqwest cliente HTTP] style TOKIO fill:#89b4fa,color:#11111b style TOWER fill:#f9e2af,color:#11111b style AXUM fill:#a6e3a1,color:#11111b style REQWEST fill:#cba6f7,color:#11111b
Entender este apilamiento es entender por qué elegir tokio como runtime arrastra decisiones: axum, reqwest, sqlx y buena parte del mundo async asumen tokio por debajo. La coordinación no es casual; es lo que permite que middleware escrito para tower funcione sin cambios en un servidor axum y en un cliente reqwest.
Las lenguas francas: traits que todos hablan
El activo más sutil del ecosistema no son las crates sino ciertos traits que se han vuelto vocabulario universal. serde::Serialize es el caso arquetípico: miles de crates lo implementan para sus tipos, y miles de formatos lo consumen, de modo que cualquier tipo «serde-compatible» habla con cualquier formato «serde-compatible» sin que sus autores se conocieran jamás. Lo mismo hacen Future (que tokio ejecuta pero cualquiera puede producir), el trait Service de tower, o Error de la std. Estas interfaces compartidas son el tejido conectivo: convierten un montón de librerías independientes en un ecosistema donde las piezas encajan.
No memorices versiones: cargo add serde --features derive edita el manifiesto por ti y elige el rango. Para descubrir el canon vivo, blessed.rs cura crates recomendadas por categoría, lib.rs las ordena por relevancia real, y This Week in Rust te mantiene al día. Antes de escribir una utilidad, comprueba si ya existe la crate que todos usan.
La austeridad de la std de Rust parece, al principio, un defecto frente a lenguajes que traen de fábrica un servidor HTTP y un parser de JSON. Pero es una de las decisiones de diseño más lúcidas del proyecto, y su lógica se revela al notar que estabilidad y evolución son garantías que tiran en direcciones opuestas. Todo lo que entra en la std adquiere un compromiso perpetuo: no podrá romper jamás, porque romper la biblioteca estándar rompería el lenguaje. Esa promesa es exactamente la correcta para lo universal e inmutable —qué es un Vec, cómo funciona Result, qué significa Iterator— pero sería una condena para dominios donde el buen diseño aún se está descubriendo. Una API de serialización o de async metida en la std en su versión temprana quedaría fosilizada en sus primeros errores, incapaz de aprender. Al dejarlas fuera, Rust no abdica de proveerlas: las reubica en un espacio —crates.io— donde el semver permite mayores, la competencia permite que la mejor idea gane, y la iteración permite corregir el rumbo. serde y tokio son hoy magníficas precisamente porque pudieron equivocarse y rehacerse fuera del corsé de la estabilidad eterna. El coste de esta arquitectura es real y no conviene endulzarlo: el recién llegado debe aprender un canon que no viene en la caja, y a veces el ecosistema se fragmenta —hubo, y hay, tensión en torno a qué runtime async es el runtime—. Pero el beneficio estructural lo supera: un núcleo pequeño y eterno sobre el que florece un ecosistema veloz y mejorable, unidos por lenguas francas —los traits compartidos— que hacen que piezas de autores desconocidos entre sí encajen sin fricción. La enseñanza para cualquier diseñador de plataformas es precisa: no metas en tu núcleo estable lo que todavía necesita evolucionar; dale una interfaz común y déjalo crecer fuera. Lo que no puede cambiar y lo que debe cambiar rápido no pueden vivir bajo la misma promesa.
La std es pequeña a propósito: garantiza estabilidad eterna y delega lo que evoluciona en crates.io. El canon fundacional: serde (serialización), tokio (runtime async), clap (CLI), rayon (paralelismo de datos), reqwest (cliente HTTP) y axum (web). La pila async se apila sobre tokio vía hyper y tower. Los traits compartidos —Serialize, Future, Service, Error— son las lenguas francas que hacen encajar crates de autores distintos. Descubre el canon con blessed.rs, lib.rs y cargo add.
- Deriva
SerializeyDeserializeen unstruct, conviértelo a JSON conserde_jsony de vuelta. Cambia a formatotomlsin tocar elstruct: comprueba que el modelo es independiente del formato. - Escribe una CLI con
clapusando#[derive(Parser)]y lanza--help: observa cuánto obtienes sin escribirlo. - Toma un cálculo pesado sobre un
Vecgrande, cambia.iter()por el.par_iter()derayony mide la diferencia contime. - Levanta un servidor mínimo con
axumytokioque responda JSON, y consúmelo desde otro binario conreqwest. - Elige una tarea (parsear CSV, medir tiempo, generar UUID) y encuentra en
lib.rsoblessed.rsla crate que la comunidad usa, en vez de escribirla tú.