Monorepositorio con Rust: organización de código en la práctica
\n\nAl desarrollar proyectos grandes en Rust, tarde o temprano surge la pregunta: ¿cómo organizar el código para que siga siendo mantenible, reutilizable y fácilmente escalable? Uno de los enfoques populares es el monorepositorio. En este artículo, analizaremos qué es un monorepositorio en el contexto de Rust, cómo configurarlo con Cargo Workspaces y qué trucos prácticos ayudarán a evitar el caos en la base de código.
El material será útil tanto para principiantes que recién comienzan a trabajar con proyectos grandes, como para desarrolladores experimentados que buscan formas efectivas de organizar el código.
\n\n¿Qué es un monorepositorio y por qué es necesario en Rust?
\n\nUn monorepositorio (monorepo) es un enfoque en el que todo el código de un proyecto (o de varios proyectos relacionados) se almacena en un solo repositorio. A diferencia de los multirrepositorios, donde cada servicio o biblioteca vive en un repositorio Git separado.
\n\n¿Por qué los desarrolladores de Rust suelen elegir un monorepositorio?
\n- \n
- Versión única de dependencias. Evita conflictos cuando diferentes crates requieren versiones distintas de la misma biblioteca. \n
- Refactorización simplificada. Un cambio de API en un crate se ve inmediatamente en todos los crates dependientes: el compilador de Rust señalará todos los lugares que deben corregirse. \n
- Compilación conjunta. Cargo puede optimizar la compilación reutilizando artefactos ya compilados. \n
- Historial de cambios unificado. No es necesario sincronizar commits entre diferentes repositorios. \n
La herramienta principal para organizar un monorepositorio en Rust es Cargo Workspaces. Veamos cómo usarlo en la práctica.
\n\nConfiguración de Cargo Workspaces: ejemplo paso a paso
\n\nSupongamos que creamos una aplicación que consta de tres partes:
\n- \n
- la biblioteca
core_lib— lógica común; \n - la biblioteca
utils— utilidades; \n - el ejecutable
app— el binario principal. \n
Creemos la estructura del monorepositorio:
\n\nmy_monorepo/\n├── Cargo.toml # manifiesto raíz del workspace\n├── core_lib/\n│ ├── Cargo.toml\n│ └── src/\n│ └── lib.rs\n├── utils/\n│ ├── Cargo.toml\n│ └── src/\n│ └── lib.rs\n└── app/\n ├── Cargo.toml\n └── src/\n └── main.rs\n\n\nPaso 1. Cargo.toml raíz
[workspace]\nmembers = [\n "core_lib",\n "utils",\n "app",\n]\n\n\nAhora los tres crates son parte de un mismo workspace. Cargo los gestionará como un todo unificado.
\n\nPaso 2. Dependencias internas
\nEn el archivo app/Cargo.toml indicaremos que depende de nuestras bibliotecas:
[package]\nname = "app"\nversion = "0.1.0"\nedition = "2021"\n\n[dependencies]\ncore_lib = { path = "../core_lib" }\nutils = { path = "../utils" }\n\n\nPaso 3. Escritura del código
\nEjemplo de código en core_lib/src/lib.rs:
pub fn greet(name: &str) -> String {\n format!("Hello, {}!", name)\n}\n\n\nEn app/src/main.rs usamos esta función:
use core_lib::greet;\n\nfn main() {\n println!("{}", greet("Rust Developer"));\n}\n\n\nPaso 4. Compilación y ejecución
\nLos comandos cargo build, cargo test y cargo run funcionan para todo el workspace. También se puede ejecutar un crate específico:
cargo run -p app\ncargo test -p core_lib\n\n\nEsta es la configuración básica. Ahora pasemos a prácticas más avanzadas.
\n\nOrganización avanzada: módulos, pruebas y versionado
\n\nCuando hay más crates, es importante mantener la disciplina. Aquí hay algunos conse