rust-sistema-modulos-estructura

Sistema de módulos en Rust: organizar archivos y crates

  • 5 min

El sistema de módulos de Rust es la forma de dividir el código en piezas jerárquicas y reutilizables.

Cuando empiezas con Rust, todo es felicidad en tu archivo src/main.rs. A medida que añades structs, enums y funciones, el archivo se vuelve inmanejable.

En otros lenguajes (como C# o Java), el nombre del archivo y su ubicación a veces son sugerencias o convenciones. En Node.js, basta con hacer require('./archivo') para cargar lo que sea.

En Rust, el sistema de módulos es explícito y jerárquico. Si no declaras un módulo, el compilador ni siquiera sabrá que ese archivo existe, aunque esté en la carpeta correcta.

Hoy vamos a ver cómo pasar del caos de un solo archivo al orden de los módulos.

¿Qué es un módulo?

Un módulo es un contenedor de código. Piensa en él como un “espacio de nombres” (namespace) que agrupa funciones, structs y otros tipos relacionados.

Antes de tocar archivos, veamos cómo se ve un módulo dentro de un solo fichero:

// src/main.rs

// Definimos un módulo llamado 'red'
mod red {
    fn conectar() {
        println!("Conectando...");
    }
}

fn main() {
    // Para usarlo, referenciamos el módulo
    // Nota: Esto fallará porque 'conectar' es privado,
    // pero ilustra la jerarquía.
    red::conectar();
}
Copied!

La palabra clave es mod. Todo lo que esté dentro de las llaves {} pertenece al módulo red.

Extraer módulos a otros archivos

Escribir módulos dentro de main.rs no soluciona el problema de tener un archivo gigante. Queremos mover red a su propio archivo.

Rust se pone estricto con esto. Para sacar el módulo red fuera, hacemos esto:

En src/main.rs: Cambiamos el bloque de código por un punto y coma.

// src/main.rs
mod red; // <--- Rust: "Busca el contenido de este módulo en otro archivo"

fn main() {
    red::conectar();
}
Copied!

En el archivo src/red.rs: Copiamos el contenido (sin la palabra mod ni las llaves).

// src/red.rs
pub fn conectar() { // Le ponemos 'pub' para poder verlo desde main
    println!("Conectando...");
}
Copied!

Error común de novato: Muchos crean el archivo red.rs pero olvidan poner mod red; en main.rs. Si haces eso, el archivo red.rs es ignorado por completo. Rust no compila todos los archivos de la carpeta, solo los que están conectados al árbol de módulos empezando por main.rs (o lib.rs).

Submódulos y carpetas

¿Y si nuestro módulo red crece y queremos dividirlo en red::servidor y red::cliente?

Necesitamos anidar módulos. Este punto ha generado bastante confusión históricamente, porque Rust cambió la forma de hacerlo en 2018, pero aún verás tutoriales viejos.

La forma preferida desde Rust 2018

Rust permite que el archivo que define el módulo tenga el mismo nombre que la carpeta que contiene los submódulos.

Estructura:

src/
├── main.rs         (declara 'mod red;')
├── red.rs          (declara 'mod servidor;' y 'mod cliente;')
└── red/
    ├── servidor.rs
    └── cliente.rs
Copied!

Veamos el código para esta estructura moderna:

src/main.rs El punto de entrada principal.

mod red; // Carga red.rs

fn main() {
    red::servidor::iniciar();
}
Copied!

src/red.rs Este archivo actúa como el “índice” de la carpeta red/.

// Buscamos los submódulos en la carpeta 'red/'
pub mod servidor;
pub mod cliente;

pub fn funcion_comun() {
    println!("Algo común a la red");
}
Copied!

src/red/servidor.rs El código real del servidor.

pub fn iniciar() {
    println!("Servidor arrancado");
}
Copied!

El árbol de módulos

Es importante visualizar tu proyecto como un árbol, no como un sistema de archivos plano.

  • La Raíz: Es src/main.rs (para binarios) o src/lib.rs (para librerías).
  • Las Ramas: Son los mod xxx; que declaras.
  • Las Hojas: Son las funciones, structs y enums.

Para acceder a cualquier elemento, seguimos la ruta (“path”) desde la raíz o de forma relativa.

// Ruta absoluta (desde la raíz del crate)
crate::red::servidor::iniciar();

// Ruta relativa (desde donde estoy)
self::servidor::iniciar();
super::otra_cosa(); // 'super' sube un nivel (como cd ..)
Copied!

Hablaremos en profundidad de pub, use y los paths en el próximo artículo, pero quédate con la idea de que la posición en el árbol determina cómo importas las cosas.

Reglas prácticas

Si quieres un módulo nuevo: Usa mod nombre; en el archivo padre.

Si el módulo es pequeño: Crea nombre.rs junto al padre.

Si el módulo tiene hijos:

  • Crea un archivo nombre.rs.
  • Crea una carpeta nombre/.
  • Mete los hijos dentro de la carpeta.
  • Declara los hijos (mod hijo;) dentro de nombre.rs.

Declara cada módulo desde su padre: Un archivo no se incorpora al árbol por existir. Su módulo padre debe declararlo con mod nombre; o mediante un bloque mod nombre { ... }.

:::