En Rust, la organización del código se basa en un sistema de módulos jerárquico que permite encapsular la lógica y controlar el acceso a los elementos (funciones, structs, enums, etc.). A diferencia de otros lenguajes donde la inclusión de archivos es automática, en Rust la jerarquía debe ser explícita: el compilador solo reconoce un módulo si ha sido declarado mediante la palabra clave mod. Un módulo puede definirse inline (dentro del mismo archivo) o en un archivo separado; si usas mod matematica, el compilador buscará src/matematica.rs o src/matematica/mod.rs.

La visibilidad es el núcleo de la seguridad de Rust. Por defecto, todos los elementos son privados al módulo donde se definen. Para permitir el acceso externo, se utiliza la palabra clave pub. Sin embargo, Rust ofrece un control granular: pub(crate) permite que un elemento sea visible en todo el crate pero no fuera de él, y pub(super) lo hace visible solo para el módulo padre. Es crucial entender que la visibilidad de un struct no implica la visibilidad de sus campos; un struct puede ser pub, pero sus campos seguirán siendo privados a menos que se marquen explícitamente como pub.

Para navegar por esta estructura, usamos rutas. Las rutas absolutas comienzan desde la raíz del crate con crate::, mientras que las rutas relativas usan self para el módulo actual o super para subir un nivel en la jerarquía.

// Módulo principal definido de forma inline (simulando un archivo src/sistema.rs)
mod sistema {
    // Un struct con visibilidad mixta
    pub struct Procesador {
        pub id: u32,          // Público: accesible desde cualquier lugar
        modelo: String,       // Privado: solo accesible en el módulo 'sistema'
    }

    impl Procesador {
        // Método público para instanciar el struct (constructor)
        pub fn nuevo(id: u32, modelo: String) -> Self {
            Self { id, modelo }
        }

        // Método público para leer el campo privado 'modelo'
        pub fn obtener_modelo(&self) -> &str {
            &self.modelo
        }
    }

    // Módulo anidado
    pub mod kernel {
        // pub(crate): Visible en todo el proyecto, pero no para usuarios externos al crate
        pub(crate) struct Driver {
            pub nombre: String,
        }

        pub fn ejecutar_tarea() {
            // Uso de ruta relativa 'super' para acceder a un elemento del módulo padre
            let _p = super::Procesador::nuevo(1, "Intel".to_string());
            println!("Kernel: Tarea ejecutada.");
        }
    }
}

// Módulo de prueba para verificar la visibilidad
mod test_modulo {
    // Uso de ruta absoluta 'crate' para importar elementos desde la raíz
    use crate::sistema::{Procesador, kernel::Driver};

    pub fn ejecutar_pruebas() {
        // 1. Instanciación mediante constructor público
        let mut proc = Procesador::nuevo(10, "AMD".to_string());

        // 2. Acceso a campo público 'id'
        proc.id = 20; 
        println!("ID procesador: {}", proc.id);

        // 3. Acceso a campo privado 'modelo' (Esto fallaría si se intentara)
        // let _ = proc.modelo; // ERROR: field `modelo` is private

        // 4. Acceso a método público para leer datos privados
        println!("Modelo: {}", proc.obtener_modelo());

        // 5. Acceso a item 'pub(crate)'
        let _d = Driver { nombre: "Red".to_string() };
        
        // 6. Acceso a función del módulo anidado
        sistema::kernel::ejecutar_tarea();
    }
}

fn main() {
    test_modulo::ejecutar_pruebas();
}

Explicación del Código

  • mod sistema: Define el módulo base. En un proyecto real, este contenido estaría en un archivo separado.
  • pub struct Procesador: Define un tipo público, pero sus campos tienen reglas distintas.
  • id: u32: Campo público; puede ser modificado desde test_modulo.
  • modelo: String: Campo privado; solo puede ser accedido por métodos dentro de sistema.
  • Procesador::nuevo: Un método pub que permite la creación de la estructura, actuando como un constructor para manejar la lógica de inicialización de campos privados.
  • Procesador::obtener_modelo: Un método pub que actúa como “getter”, permitiendo leer modelo sin romper el encapsulamiento.
  • mod kernel: Un módulo hijo de sistema.
  • pub(crate) struct Driver: Gracias a pub(crate), el struct Driver es visible dentro de test_modulo porque ambos pertenecen al mismo crate, pero no sería visible si este código fuera una librería usada por otro proyecto.
  • super::Procesador: La palabra clave super sube un nivel en la jerarquía, permitiendo que kernel vea a Procesador.
  • use crate::sistema::...: La ruta absoluta crate salta directamente a la raíz para buscar la definición de sistema.
  • test_modulo::ejecutar_pruebas(): La función principal que orquesta el acceso a todos los niveles de visibilidad probados.

75