Cargo y crates en Rust
En la página Empezar con Rust usaste cargo new y cargo run. Eso fue solo el principio. Cargo es la herramienta de compilación de Rust y también su gestor de paquetes. Compila tu código, ejecuta tus pruebas, da formato a tus archivos y descarga bibliotecas que han escrito otras personas.
Esas bibliotecas se llaman crates (paquetes de código). Piensa en los crates como las apps de una tienda de aplicaciones: alguien ya resolvió un problema (leer JSON, hacer peticiones web, procesar fechas) y tú puedes añadir su solución a tu proyecto con un solo comando. En esta página verás cómo funciona y cómo elegir un buen crate.
Crates y paquetes
Sección titulada «Crates y paquetes»Hay dos palabras que aparecen todo el tiempo:
- Un crate es una unidad de código Rust que compila el compilador. Puede ser un binario (un programa que puedes ejecutar, que empieza en
src/main.rs) o una biblioteca (código para que lo usen otros programas, que empieza ensrc/lib.rs). - Un paquete (package) es lo que crea
cargo new: una carpeta con un archivoCargo.tomly uno o más crates dentro.
En el día a día, la gente dice “crate” para las dos cosas. Cuando alguien dice “usa el crate serde”, quiere decir “añade el paquete serde de crates.io a tu proyecto”.
Dentro de Cargo.toml
Sección titulada «Dentro de Cargo.toml»Cada paquete tiene un archivo Cargo.toml. Está escrito en TOML, un formato de configuración sencillo. Esto es lo que te da cargo new hello_rust:
[package]name = "hello_rust"version = "0.1.0"edition = "2024"
[dependencies][package]describe tu proyecto.nameyversionson lo que vería la gente si lo publicaras.editionelige qué edición del lenguaje Rust usar. Las ediciones (2018, 2021, 2024) permiten que Rust mejore sin romper el código antiguo. Deja la que haya escritocargo new.[dependencies]enumera los crates que usa tu código. Al principio está vacía.
A medida que el proyecto crece, puedes ver más secciones:
[dependencies]serde = { version = "1.0", features = ["derive"] }serde_json = "1.0"
[dev-dependencies]# solo lo usan las pruebas y los ejemplos, no tu programa realpretty_assertions = "1.4"
[profile.release]# configuración para `cargo build --release`opt-level = 3Añadir una dependencia con cargo add
Sección titulada «Añadir una dependencia con cargo add»Puedes editar [dependencies] a mano, pero lo más fácil es cargo add. Busca la última versión en crates.io y escribe la línea por ti:
cargo add serde_jsonAlgunos crates tienen partes opcionales llamadas features (funcionalidades opcionales). Vienen desactivadas por defecto para que tu compilación sea más ligera. Las activas con --features:
cargo add serde --features deriveDespués de estos dos comandos, tu Cargo.toml tendrá algo parecido a esto:
[dependencies]serde = { version = "1.0.228", features = ["derive"] }serde_json = "1.0.145"Ahora usa el crate en tu código. El siguiente cargo run lo descarga, lo compila una vez y lo enlaza con tu programa:
use serde_json::json;
fn main() { let crab = json!({ "legs": 10, "name": "Ferris" });
println!("{}", crab); println!("Nombre: {}", crab["name"]);}Salida:
{"legs":10,"name":"Ferris"}Nombre: "Ferris"Las comillas alrededor de "Ferris" aparecen porque crab["name"] sigue siendo un valor JSON, no un string normal de Rust.
El Playground de Rust ya incluye los crates más populares, así que este ejemplo funciona ahí sin cargo add. En tu propio equipo, primero necesitas el paso cargo add serde_json. Aprenderás mucho más sobre este crate en la página JSON con Serde.
Para quitar un crate que ya no necesitas, ejecuta cargo remove serde_json.
Requisitos de versión y Cargo.lock
Sección titulada «Requisitos de versión y Cargo.lock»Cuando escribes serde_json = "1.0", no estás pidiendo exactamente la versión 1.0. Estás diciendo “cualquier versión compatible con la 1.0”. Cargo sigue el versionado semántico (SemVer): una versión tiene la forma MAJOR.MINOR.PATCH (mayor, menor, parche), y solo un cambio en el número mayor puede romper tu código.
| Escribes | Cargo puede usar | Por qué |
|---|---|---|
"1.0" (igual que "^1.0") |
>=1.0.0 y <2.0.0 |
todas las versiones 1.x deberían ser compatibles |
"1.4.2" |
>=1.4.2 y <2.0.0 |
al menos la 1.4.2, pero siempre 1.x |
"0.4" |
>=0.4.0 y <0.5.0 |
antes de la 1.0, el número menor cuenta como cambio incompatible |
"~1.4" |
>=1.4.0 y <1.5.0 |
solo actualizaciones de parche |
"=1.4.2" |
exactamente 1.4.2 |
casi nunca hace falta |
Entonces, ¿quién decide la versión exacta? Ese es el trabajo de Cargo.lock. La primera vez que compilas, Cargo elige las versiones más nuevas que cumplen tus requisitos y las apunta en Cargo.lock. A partir de ahí, todas las compilaciones usan exactamente esas mismas versiones, en tu equipo y en el de cualquier otra persona.
- No edites
Cargo.locka mano. Cargo se encarga de él. - Súbelo a git, sobre todo si es un programa. Así tus compañeros y tu CI compilan exactamente lo mismo que tú.
cargo updatepasa a las versiones más nuevas que sigan cumpliendoCargo.tomly reescribeCargo.lock. Nunca salta a una versión mayor nueva; para eso tienes que cambiarCargo.tomltú mismo.
Encontrar un buen crate
Sección titulada «Encontrar un buen crate»Hay tres sitios web que usarás una y otra vez:
- crates.io es el registro oficial. Todos los crates públicos están aquí. Busca una palabra como “csv” o “date”.
- docs.rs aloja la documentación de todos los crates, generada automáticamente. Por ejemplo, docs.rs/serde_json.
- lib.rs es un índice no oficial y más rápido que agrupa los crates por categorías. Es ideal para explorar.
Muchos crates resuelven el mismo problema, así que ¿cómo eliges? Antes de añadir uno, tómate un minuto para revisar:
- Descargas. Los crates populares tienen millones de descargas. Eso significa que mucha gente encontró y corrigió los errores antes que tú.
- Actualizaciones recientes. Mira la fecha de la última versión. Un crate que no ha cambiado en años puede estar bien (algunos simplemente están “terminados”), pero comprueba que su repositorio no esté lleno de issues sin respuesta.
- Documentación. Abre su página en docs.rs. Los buenos crates tienen una portada clara con un ejemplo que puedes copiar.
- Versión 1.0 o superior. No es obligatorio, pero te indica que el autor promete una API estable.
- Pocas dependencias. Cada crate que añades trae sus propias dependencias. Cuanto más pequeño, más fácil es confiar en él.
Si tienes dudas, busca “rust” más tu problema y mira qué crate recomienda la comunidad una y otra vez.
Comandos esenciales de Cargo
Sección titulada «Comandos esenciales de Cargo»No hace falta que te los aprendas de memoria. Ten esta tabla a mano y los irás aprendiendo con el uso:
| Comando | Qué hace |
|---|---|
cargo new my_app |
Crea un proyecto binario nuevo (añade --lib para una biblioteca) |
cargo build |
Compila en target/debug/ |
cargo run |
Compila y ejecuta (los argumentos van después de --: cargo run -- input.txt) |
cargo check |
Busca errores sin generar el programa. Mucho más rápido que build |
cargo test |
Ejecuta tus pruebas (consulta Pruebas en Rust) |
cargo fmt |
Da formato a todo tu código con el estilo estándar |
cargo clippy |
Ejecuta el linter. Te sugiere código más simple e idiomático |
cargo doc --open |
Genera la documentación HTML de tu proyecto y de cada dependencia, y la abre |
cargo update |
Actualiza Cargo.lock a las versiones compatibles más nuevas |
cargo build --release |
Compila un programa optimizado en target/release/ |
Un buen hábito: ejecuta cargo check a menudo mientras escribes, y cargo fmt y cargo clippy antes de hacer commit. cargo doc --open también es una joya escondida: te da la documentación de las versiones exactas de los crates que usas, y funciona sin conexión.
Compilación debug y release
Sección titulada «Compilación debug y release»Por defecto, Cargo hace una compilación debug (de depuración). Compila rápido y mantiene comprobaciones de seguridad extra, como provocar un panic cuando un entero se desborda. Es perfecta mientras desarrollas.
Una compilación release (cargo build --release o cargo run --release) tarda más en compilar, pero se ejecuta mucho más rápido, a menudo 10 veces o más. Úsala cuando distribuyas tu programa o midas su velocidad.
Tu código incluso puede saber qué tipo de compilación es:
fn main() { if cfg!(debug_assertions) { println!("Compilación debug: las comprobaciones extra están activadas"); } else { println!("Compilación release: optimizada para la velocidad"); }}Salida:
Compilación debug: las comprobaciones extra están activadasEn el Playground puedes cambiar de “Debug” a “Release” en el menú y volver a ejecutarlo para ver la otra línea.
Crates binario y de biblioteca juntos
Sección titulada «Crates binario y de biblioteca juntos»Un paquete puede contener una biblioteca y un binario al mismo tiempo. Es una organización muy común: pones la lógica de verdad en src/lib.rs, y src/main.rs se queda pequeño y solo la llama:
greeter/├── Cargo.toml└── src/ ├── lib.rs # el crate de biblioteca: lógica reutilizable └── main.rs # el crate binario: el programa que ejecutaspub fn greet(name: &str) -> String { format!("¡Hola, {name}!")}fn main() { // la biblioteca se llama igual que el paquete println!("{}", greeter::greet("Ferris"));}¿Por qué dividirlo así? El código de lib.rs es fácil de probar, y otros programas (o un segundo binario) pueden reutilizarlo. El Playground solo tiene un archivo, pero puedes conseguir algo parecido con un pub mod, como en la página de Módulos en Rust:
mod greeter { pub fn greet(name: &str) -> String { format!("¡Hola, {name}!") }}
fn main() { println!("{}", greeter::greet("Ferris"));}Salida:
¡Hola, Ferris!Un vistazo rápido a los workspaces
Sección titulada «Un vistazo rápido a los workspaces»Cuando un proyecto crece hasta tener varios paquetes que van juntos (por ejemplo, una app de línea de comandos, un servidor web y una biblioteca compartida), puedes ponerlos en un workspace (espacio de trabajo). Comparten un único Cargo.lock y una única carpeta target/, así que todo se compila junto y usa las mismas versiones de las dependencias.
El Cargo.toml principal enumera los miembros:
[workspace]members = ["app", "core"]resolver = "3"my_project/├── Cargo.toml # el archivo del workspace de arriba├── Cargo.lock # compartido por todos los miembros├── app/ # un paquete binario└── core/ # un paquete de bibliotecaAsí, cargo build en la carpeta principal lo compila todo, y cargo run -p app ejecuta un solo miembro. No lo necesitarás en tus primeros proyectos, pero viene bien reconocerlo cuando leas código de otras personas.
Errores comunes
Sección titulada «Errores comunes»Usar un crate sin añadirlo. Si escribes use serde_json::json; pero olvidaste cargo add serde_json, el compilador no lo encuentra:
error[E0432]: unresolved import `serde_json` --> src/main.rs:1:5 |1 | use serde_json::json; | ^^^^^^^^^^ use of unresolved module or unlinked crate `serde_json` | = help: you might be missing a crate named `serde_json`El compilador te dice que no puede resolver la importación serde_json y que quizá te falta un crate con ese nombre. La solución es ejecutar cargo add serde_json. Revisa también cómo está escrito: los nombres de los crates usan - o _, y en el código un - siempre se convierte en _.
Olvidar una feature. Si #[derive(Serialize)] dice que no encuentra la macro derive, probablemente añadiste serde sin --features derive. Vuelve a ejecutar cargo add serde --features derive; actualiza la línea que ya existe.
Medir la velocidad con una compilación debug. Si tu programa parece lento, prueba cargo run --release antes de cambiar nada del código.
Práctica
Sección titulada «Práctica»1. Usa un crate desde el Playground
Sección titulada «1. Usa un crate desde el Playground»Usa la macro json! de serde_json para describir un crate con tres claves: downloads (un número), name y version. Imprime el valor completo y después solo la versión.
Pista: copia la forma del ejemplo de crab en Añadir una dependencia con cargo add. En tu equipo, ejecuta antes cargo add serde_json; el Playground ya lo incluye.
Ver solución
use serde_json::json;
fn main() { let krate = json!({ "downloads": 500, "name": "serde_json", "version": "1.0" });
println!("{}", krate); println!("Versión: {}", krate["version"]);}Salida:
{"downloads":500,"name":"serde_json","version":"1.0"}Versión: "1.0"La variable se llama krate porque crate es una palabra reservada en Rust. Esa forma de escribirlo es una broma habitual en el código Rust. Igual que antes, la versión sale entre comillas porque sigue siendo un valor JSON; la página JSON con Serde explica cómo obtener el texto sin comillas.
2. Mensajes solo en compilaciones debug
Sección titulada «2. Mensajes solo en compilaciones debug»Escribe una función debug_log(msg: &str) que imprima [debug] seguido del mensaje, pero solo en una compilación debug. Úsala en main alrededor de un cálculo pequeño e imprime el resultado de forma normal.
Pista: envuelve el println! en if cfg!(debug_assertions) { ... }, como en Compilación debug y release.
Ver solución
fn debug_log(msg: &str) { if cfg!(debug_assertions) { println!("[debug] {msg}"); }}
fn main() { debug_log("empieza el cálculo"); let mut total = 0; for n in 1..=10 { total += n; } debug_log("cálculo terminado");
println!("La suma de 1 a 10 es {total}");}Salida:
[debug] empieza el cálculo[debug] cálculo terminadoLa suma de 1 a 10 es 55Cambia el Playground a “Release” (o usa cargo run --release en tu equipo) y vuelve a ejecutarlo: solo se imprime la última línea. Para proyectos reales, la página de Logging muestra crates que hacen esto como es debido, con niveles como debug e info.
Resumen
Sección titulada «Resumen»- Cargo compila, ejecuta, prueba y da formato a tu código, y descarga crates de crates.io.
Cargo.tomles lo que pides;Cargo.lockapunta las versiones exactas que obtuviste. Sube los dos a git.cargo add nombre --features xañade una dependencia."1.0"significa “cualquier versión 1.x compatible”.- Juzga un crate por sus descargas, sus actualizaciones recientes y una buena documentación en docs.rs.
- Usa
cargo checkmientras escribes,cargo fmtycargo clippyantes de hacer commit, y--releasecuando importe la velocidad. - Pon la lógica reutilizable en
src/lib.rsy manténsrc/main.rspequeño.
Comprueba lo que aprendiste
Sección titulada «Comprueba lo que aprendiste»Comprueba lo aprendido
1. En Cargo.toml, ¿qué significa serde_json = "1.0"?
2. ¿Qué archivo apunta las versiones exactas de cada dependencia que usó tu compilación?
3. Quieres buscar errores en tu código lo más rápido posible, sin generar un programa. ¿Qué comando es el más adecuado?
4. ¿Cómo añades serde con su feature derive activada?
Puntuación: 0 / 4
Para leer más
Sección titulada «Para leer más»- The Cargo Book (en inglés): la guía y referencia oficial de todo lo que hace Cargo.
- Specifying Dependencies — The Cargo Book (en inglés): todas las formas de escribir un requisito de versión, además de dependencias desde git o desde una ruta local.
cargo add— The Cargo Book (en inglés): todas las opciones decargo add, como--dev,--featuresy--no-default-features.- More about Cargo and Crates.io — The Rust Book (en inglés): perfiles de compilación, cómo publicar tu propio crate e instalar herramientas con
cargo install. - Cargo Workspaces — The Rust Book (en inglés): un workspace paso a paso con un binario y dos bibliotecas.