cargo, el gestor todo-en-uno
cargo new, check, build, run y test: qué hace exactamente cada orden, en qué se diferencian, y cómo Cargo.toml y Cargo.lock gobiernan tus dependencias con semver.
En otros lenguajes juntas a mano un compilador, un gestor de paquetes, un runner de tests y un generador de documentación, cada uno con su cultura y sus rarezas. En Rust todo eso es una sola herramienta: cargo. Aprender sus verbos es aprender el flujo de trabajo entero.
- Crear paquetes binarios y de librería con
cargo new. - Dominar el ciclo
check,buildyrun, y cuándo usar cada uno. - Ejecutar la batería de pruebas con
cargo test. - Declarar dependencias en
Cargo.tomly entender semver y el lockfile.
Crear un paquete
cargo new hola # paquete binario (con src/main.rs)
cargo new --lib mi_lib # paquete de librería (con src/lib.rs)
cargo init # inicializa cargo en un directorio ya existente
cargo new hola genera la estructura mínima y un repositorio git:
hola/
├── Cargo.toml # el manifiesto: metadatos y dependencias
├── .gitignore # ignora /target
└── src/
└── main.rs # el punto de entrada
El ciclo diario: check, build, run
Estas tres órdenes son el 90% de tu día, y confundirlas cuesta tiempo:
flowchart TD S[codigo y Cargo.toml] --> C[cargo check verifica tipos] S --> B[cargo build genera binario] B --> R[cargo run ejecuta] B --> T[cargo test compila y prueba]
cargo checkanaliza y comprueba tipos y borrow checker, pero no genera el binario final. Es varias veces más rápido quebuild: es tu bucle de feedback mientras escribes.cargo buildcompila de verdad. Sin flags, produce un binario debug entarget/debug/(rápido de compilar, sin optimizar, con símbolos). Con--releaseproduce uno optimizado entarget/release/(lento de compilar, veloz de ejecutar).cargo runhacebuildsi hace falta y ejecuta el binario. Acepta los mismos flags:cargo run --release.
cargo check # el más rápido: solo valida
cargo build # binario debug
cargo build --release # binario optimizado
cargo run -- arg1 arg2 # ejecuta; lo que va tras -- son args del programa
Deja cargo check (o rust-analyzer, que lo hace por ti) corriendo mientras programas. Como omite la generación de código máquina, te da los errores de tipos y de ownership en una fracción del tiempo de un build completo. Reserva build --release para medir rendimiento o publicar.
Probar: cargo test
cargo test compila un harness especial y ejecuta todo lo que sea una prueba: funciones marcadas con #[test], los tests de integración en tests/ y hasta los ejemplos de tu documentación.
pub fn suma(a: i32, b: i32) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn suma_positivos() {
assert_eq!(suma(2, 3), 5);
}
}
cargo test # toda la batería
cargo test suma_positivos # solo los tests cuyo nombre contenga eso
cargo test -- --nocapture # deja ver los println de los tests
El #[cfg(test)] hace que ese módulo solo se compile al probar: no pesa ni un byte en tu binario final.
Cargo.toml y las dependencias
El manifiesto declara qué es tu paquete y de qué depende:
[package]
name = "hola"
version = "0.1.0"
edition = "2024"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
rand = "0.9"
[dev-dependencies]
criterion = "0.5" # solo para tests y benchmarks
La forma más cómoda de añadir dependencias es dejar que cargo edite el manifiesto:
cargo add serde --features derive
cargo add tokio --features full
Una versión como "1.0" es, por defecto, un requisito caret: equivale a >=1.0.0, <2.0.0. Cargo elige la versión más alta compatible y la congela en Cargo.lock, con hashes y versiones exactas de todo el árbol de dependencias.
Más allá de las dependencias, el manifiesto controla cómo se compila. Los perfiles ajustan la optimización, y un workspace agrupa varios crates que comparten Cargo.lock y el directorio target/:
[profile.release]
opt-level = 3 # optimización agresiva
lto = true # link-time optimization entre crates
codegen-units = 1 # compila más lento, binario más rápido
[workspace]
members = ["app", "core", "cli"]
Para una aplicación (un binario), commitea Cargo.lock: garantiza que todos compilan bits idénticos. Para una librería el criterio clásico era no versionarlo, aunque hoy se recomienda hacerlo también para que tu CI sea reproducible. cargo update recalcula el lockfile dentro de los rangos de semver.
Cargo no es un lujo: es una de las razones por las que Rust se adopta tan rápido. Un manifiesto declarativo, un resolvedor de dependencias con semver de verdad, un lockfile reproducible, un runner de tests integrado, doctests que garantizan que tus ejemplos compilan, y cargo doc, cargo bench, cargo tree y cargo add, todo con la misma interfaz y sin configuración. Compáralo con el mundo de C, donde el build system, el gestor de paquetes y el runner de tests son decisiones separadas que cada proyecto resuelve a su manera. La consecuencia es cultural: como todos usan cargo, cualquier crate de crates.io se compila con cargo build sin leer instrucciones. Esa uniformidad —una sola herramienta que todos comparten— es un activo de ingeniería tan valioso como el propio borrow checker. Dominar sus verbos no es aprender un comando: es adoptar el flujo de trabajo estándar de todo un ecosistema.
- Crea un paquete binario con
cargo newe inspecciona elCargo.tomly el árbol de archivos. - Lanza
cargo check,cargo buildycargo build --release, y compara qué aparece entarget/. - Añade
randconcargo addy úsalo para imprimir un número aleatorio concargo run. - Escribe una función con un
#[test]y córrelo concargo test. - Mira el árbol de dependencias con
cargo treey abre la doc de tu crate concargo doc --open.