Módulos y visibilidad en Rust: mod, pub, use, paths y cómo organizar el código

El sistema de módulos de Rust organiza el código en namespaces jerárquicos y controla qué partes son visibles desde fuera. Es distinto a otros lenguajes: en Rust no hay un mapeo uno a uno obligatorio entre fichero y módulo. Puedes tener módulos inline, módulos en ficheros separados, y controlar visibilidad con distintos niveles de pub.

Módulos inline

// Un módulo puede vivir dentro del mismo fichero
mod matematicas {
    pub fn sumar(a: i32, b: i32) -> i32 { a + b }

    // Esta función es privada al módulo
    fn helper(x: i32) -> i32 { x * 2 }

    pub mod avanzado {
        pub fn potencia(base: i32, exp: u32) -> i32 {
            base.pow(exp)
        }
    }
}

fn main() {
    println!("{}", matematicas::sumar(3, 4));             // 7
    println!("{}", matematicas::avanzado::potencia(2, 8)); // 256
    // matematicas::helper(5); // ERROR: es privada
}

Módulos en ficheros

// src/main.rs (o src/lib.rs)
mod matematicas;  // busca src/matematicas.rs o src/matematicas/mod.rs
mod utils;

use matematicas::sumar;

fn main() {
    println!("{}", sumar(1, 2));
    utils::log("iniciado");
}

// src/matematicas.rs
pub fn sumar(a: i32, b: i32) -> i32 { a + b }
pub fn restar(a: i32, b: i32) -> i32 { a - b }

// Submódulo dentro de matematicas/
// src/matematicas/avanzado.rs
pub fn raiz_cuadrada(x: f64) -> f64 { x.sqrt() }

// src/matematicas/mod.rs (alternativa al fichero plano)
pub mod avanzado;
pub fn sumar(a: i32, b: i32) -> i32 { a + b }

Visibilidad: pub, pub(crate), pub(super)

mod exterior {
    pub fn publica() {}              // visible desde cualquier lugar
    pub(crate) fn solo_crate() {}   // visible solo dentro del crate
    pub(super) fn para_padre() {}   // visible solo en el módulo padre
    fn privada() {}                 // solo en este módulo

    mod interior {
        pub fn desde_interior() {
            super::privada();        // interior puede acceder al padre
            super::para_padre();     // también pub(super)
        }
    }
}

fn main() {
    exterior::publica();
    exterior::solo_crate(); // OK (estamos en el mismo crate)
    // exterior::para_padre(); // ERROR: solo visible para su padre directo
}

Paths absolutos y relativos

mod a {
    pub fn foo() {}

    pub mod b {
        pub fn bar() {
            // Path relativo: desde el módulo actual
            super::foo();

            // Path absoluto desde la raíz del crate
            crate::a::foo();
        }
    }
}

fn main() {
    // Desde la raíz ambos son equivalentes
    a::b::bar();
    crate::a::b::bar();
}

use y re-exportaciones con pub use

// Traer nombres al scope actual
use std::collections::{HashMap, HashSet};
use std::io::{self, Write}; // self trae io, Write trae io::Write

fn main() {
    let mut m: HashMap<String, i32> = HashMap::new();
    m.insert("uno".into(), 1);
}

// Re-exportar para simplificar la API pública
// src/lib.rs
mod interno {
    pub struct Punto { pub x: f64, pub y: f64 }
    pub fn nueva_conexion() -> String { "conexión".into() }
}

// Re-exportar en el nivel del crate para que los usuarios no vean internal::
pub use interno::Punto;
pub use interno::nueva_conexion;

// Ahora los usuarios importan directamente:
// use mi_crate::Punto;
// use mi_crate::nueva_conexion;

Separación lib.rs y main.rs

// Estructura recomendada para crates con binario y librería:
// src/
// ??? lib.rs    ? lógica reutilizable (testeable, importable)
// ??? main.rs   ? punto de entrada que usa la librería

// src/lib.rs
pub mod config;
pub mod procesador;

pub use procesador::Procesador;

// src/main.rs
use mi_crate::Procesador;

fn main() {
    let p = Procesador::new();
    p.ejecutar();
}

// Ventajas:
// - Los integration tests en tests/ pueden importar mi_crate sin problemas
// - La CLI queda separada de la lógica de negocio
// - Puedes publicar la librería en crates.io y añadir el binario luego

Alias con use ... as

use std::fmt::Result as FmtResult;
use std::io::Result as IoResult;

// Sin alias habría conflicto de nombres
fn formato() -> FmtResult { Ok(()) }
fn escritura() -> IoResult<()> { Ok(()) }

Resumen

  • mod nombre; busca el fichero src/nombre.rs o src/nombre/mod.rs.
  • Sin pub, un item es privado a su módulo; con pub, es accesible desde fuera.
  • pub(crate) limita la visibilidad al crate; pub(super), solo al módulo padre directo.
  • crate:: para paths absolutos desde la raíz; super:: para subir un nivel.
  • pub use re-exporta items internos para simplificar la API pública.
  • Separar lib.rs y main.rs facilita los tests y la reutilización de la lógica.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP