Rusty Full Stack

El blog para los amantes de Rust, Ionic y Vuejs

Jaime Blandón
Jaime Blandón Desarrollador de software desde 2009, entusiasta de Rust, Vuejs y Ionic!. Fundador de este blog, espero que las publicaciones te sean de utilidad y si tienes comentarios para mejorar, son bienvenidos.

Organizar Código En Rust - Diferentes Archivos

Hola, después de una pausa un poco prolongada, por fin tenemos el último post sobre la organización de código en Rust. Si quieres ver los capítulos anteriores puedes explorarlo acá:

[Parte 1] - Organizar Código En Rust - Paquetes y Librerías.

[Parte 2] - Organizar Código En Rust - Módulos

En el post anterior nos detuvimos a estudiar los módulos e hicimos un ejemplo creando un pequeño programa que simulaba una caja de supermercado, puedes encontrar el código completo del ejemplo en el repositorio en github dando click a este enlace.

Hasta el momento, nos hemos concentrado en paquetes, librerías y módulos, y hemos construido nuestros propios crates, sin embargo, hasta el momento hemos concentrado nuestro código en un archivo lib.rs o en un archivo main.rs, pero Rust, nos permite organizar nuestro código en multiples archivos, tal y como se tratase de python, Java, javascript, y muchos otros, cada archivo le conoceremos como un módulo (el cual estudiamos en el segundo post de esta mini-serie).

Te invito a que revises el código de la caja de supermercado y verás que la estructura de archivos es muy similar a esta:

En la cual todo se concentra únicamente en dos archivos, lib.rs y main.rs

También te invito a echarle un vistazo el código de ambos archivos:

Verás que si bien los hemos logrado organizar en funciones y módulos independientes (compras y pagos en el archivo lib.rs) el código continua siendo un poco extenso y a lo mejor se puede organizar de mejor forma en diferentes archivos.

Una propuesta para este ejemplo seria organizar el código de esta forma, más adelante explico un poco mi idea:

La estructura de archivos anterior, funcionará exactamente como la versión en el repositorio en github que coloqué unos cuantos párrafos atrás, si bien parecería que ahora hay demasiados archivos a comparación con la del repo, donde solamente hay dos archivos, lib.rs y main.rs, al momento de construirlo, veremos que esta división de archivos, nos facilitará entender y leer mucho más fácilmente el código, además de dejarlo mucho mejor organizado.

Empecemos a construir nuestro ejemplo para el cual puedes ver el código terminado en el repositorio de github dando click a este enlace, esta vez no me detendré mucho en los detalles de la funcionalidad, puesto que está explicado en el post anterior (te invito a leerlo si no lo has hecho 😇), pero sí intentaré explicar como funcionan los "use" y el concepto del módulo para cada caso.

Iniciemos creando un nuevo proyecto en la carpeta de nuestra preferencia, ejecutamos el comando:

cargo new caja-supermercado-multiarchivos

cd caja-supermercado-multiarchivos

Genial, ahora vamos a constuir la estructura de carpetas que vamos a necesitar. En nuestro proyecto, crea los siguientes folders y por el momento déjalos vacios.

> caja-supermercado-multiarchivos

       > src

              > menu

                     > manejadores

              > models

              > operaciones

              > utileria

              main.rs

              Cargo.toml

Tu proyecto debería de verse más o menos así:

  Ok, ahora vamos a crear nuestros "modelos", los modelos no serán más que las definiciones de las estructuras para los items de la compra, y las necesarias para el módulo de pagos, vamos a dividir eso en dos archivos, dentro de src/models/ vamos a crear dos archivos modelo_compras.rs y modelo_pagos.rs.

Tu estructura debería de verse así:

En los archivos vamos a incorporar las estructuras y enums que habíamos definido en nuestro ejemplo del capítulo anterior, el código de cada archivo debería de quedarte así:

        
        
            // src/models/modelo_compras.rs

#[derive(Debug)]
pub struct Item {
    pub nombre: String, // Nombre del item
    pub precio_unitario: f32, // Precio Unitario del item
    pub cantidad: f32, // Cantidad a comprar del item, es float porque pueden ser fracciones de unidades, como kilos
}

        
        
    
        
        
            // src/models/modelo_pagos.rs

pub enum MetodoDePago {
    Efectivo,
    Tarjeta,
    TransferenciaBancaria,
}

pub struct ResultadoPago {
    pub metodo_pago: String, // La descripcion del metodo de pago
    pub fue_exitoso: bool, // true si el pago se pudo hacer o false si no se pudo hacer
    pub cambio: f32, // Cambio a devolver al cliente.
}

        
        
    

Listo!, hasta el momento nada fuera de lo común, pero ahora ¿cómo hacemos saber a nuestro crate root (main.rs) que ambos archivos existen y que pueden utilizarse?

Archivos mod.rs

existen varias formas de utilizar los archivos que hemos creado, una puede ser simplemente importándolos utilizando

crate::models::modelo_compras;

Sin embargo, lo anterior funcionaría si queremos importar modelo_compras en otros archivos distintos al main.rs o lib.rs, para cualquiera de los dos, debemos hacerles saber que existen, una forma que particularmente me gusta es agregar un archivo mod.rs dentro de las carpetas que contienen los módulos o archivos que hemos creado.

Puedes comparar un archivo mod.rs como un archivo index.js en el mundo de javascript, en el cual se hacen los exports para hacerle saber a tu proyecto que existen clases, funciones, etc.

Vamos a crear nuestro archivo mod.rs, lo vamos a ubicar dentro de src/models/ que es justamente donde están los dos módulos que estamos creando. Tu proyecto debería de verse así:

Ahora dentro de nuestro mod.rs vamos a colocar las siguientes líneas de código, que le permitirán a nuestro proyecto, saber que existen los módulos de modelo_compras y modelo_pagos, y que además los módulos son públicos:

        
        
            // src/models/mod.rs

pub mod modelo_compras;
pub mod modelo_pagos;

        
        
    

Bastante parecidos a un index.js no? 😎

Ahora, otro paso importando es que cada módulo debe definirse en las primeras líneas de nuestro crate root (main.rs) también, de otro modo, el compilador nos dirá que eso nos hace falta, vamos a hacerlo.

        
        
            // src/main.rs

mod models;
use models::modelo_compras;

fn main() {

    let mut items_compra: Vec<modelo_compras::Item> = Vec::new();
    println!("Hello, world!");
}

        
        
    

Es importante ver las primeras dos líneas, en la primera

mod models;

le hacemos saber el crate root que existe el módulo llamado "models" y que está habilitado para cualquier otra parte del crate (ojo, si no se coloca esa línea, el compilador no permitirá las importaciones del módulo en cualquier otro punto de nuestro proyecto).

La segunda línea define que main.rs quiere utilizar lo que se encuentre dentro de modelo_compras

use models::modelo_compras;

Si miras dentro de la función main, verás que ahora es posible utilizar la estructura "Item" dentro de modelo_compras. También es posible hacerle un use directamente a la estructura "Item" con:

use models::modelo_compras::Item;

Pero lo más común es que lo dejes a nivel de un módulo para poder identificar de donde procede una estructura, función, etc. Sin embargo esto queda a tu criterio, ambas maneras funcionan bien.

Perfecto ya tenemos nuestro primera división de archivos 🤩

Haciendo uso de módulos en distintos archivos en Rust.

Ahora vamos a continuar agregando más funcionalidad

Anteriormente teníamos dos módulos con funcionalidades separadas de compras y pagos, sin embargo, aunque estaban separadas, las funcionalidades se encontraban dentro de lib.rs (en nuestro ejemplo del post anterior, ahora moveremos las funcionalidades a dos archivos diferentes, esto lo incorporaremos dentro del folder src/operaciones 

Vamos a crear de una vez el archivo mod.rs para esa carpeta. Los archivos a crear son:

  • compras.rs
  • pagos.rs
  • mod.rs

Debería de verse de esta forma:

Ahora vamos a agregar toda la funcionalidad de pagos.rs que recuerda que es únicamente simulado, es importante mencionarte que vamos a utilizar las estructuras dentro de src/models/modelo_pagos.rs. Esto nos ayudará a comprender como podemos utilizar un módulo dentro de otro cuando lo tenemos divididos por archivos.

        
        
            // src/operaciones/pagos.rs

use crate::models::modelo_pagos;

pub fn pagar(metodo_de_pago: modelo_pagos::MetodoDePago, monto_a_pagar: f32, recibido_del_cliente: f32, tarjeta: &str) -> modelo_pagos::ResultadoPago{
    // El parametro metodo_de_pago es la forma de pago elegida por el cliente.
    // El parametro monto_a_pagar es el total a pagar de la compra.
    // recibido_del_cliente es la cantidad de dinero recibida del cliente, si no es efectivo, es igual al monto a pagar
    // tarjeta, es el numero de tarjeta del cliente, si el pago es en efectivo o por transferencia, no es necesario, puede ser cualquiera

    // Ahora, dependiendo del metodo de pago elegido por el cliente, invocamos las funciones privadas, esto puede hacerse
    // porque estan dentro del mismo alcance de este metodo.
    let resultado = match metodo_de_pago {
        modelo_pagos::MetodoDePago::Efectivo => pago_en_efectivo(monto_a_pagar, recibido_del_cliente),
        modelo_pagos::MetodoDePago::Tarjeta => pago_con_tarjeta(monto_a_pagar, tarjeta),
        modelo_pagos::MetodoDePago::TransferenciaBancaria => pago_por_transferencia_bancaria(monto_a_pagar)
    };

    resultado
}

fn pago_en_efectivo(monto_a_pagar: f32, recibido_del_cliente: f32) -> modelo_pagos::ResultadoPago {
    // Si el pago es en efectivo, se calculara el cambio a devolver al cliente
    let mut cambio = recibido_del_cliente - monto_a_pagar;
    // redondeando a dos decimales
    let y = 10i32.pow(2) as f32;
    cambio = (cambio * y).round() / y;

    modelo_pagos::ResultadoPago {
        metodo_pago: String::from("En Efectivo"),
        fue_exitoso: true,
        cambio
    }
}

fn pago_con_tarjeta(monto_a_pagar: f32, numero_tarjeta: &str) -> modelo_pagos::ResultadoPago {
    // Si el pago es con tarjeta, simularemos el resultado

    // Aca se estaria procesando todo aquello critico a nivel de seguridad.
    println!("Realizando el pago con el servicio de tarjeta credito/debito");
    println!("Monto a pagar: {}", monto_a_pagar);
    println!("Tarjeta: {}", numero_tarjeta);

    //Ahora simulamos el resultado
    modelo_pagos::ResultadoPago {
        metodo_pago: String::from("Tarjeta"),
        fue_exitoso: true,
        cambio: 0.0
    }
}

fn pago_por_transferencia_bancaria(monto_a_pagar: f32) -> modelo_pagos::ResultadoPago {
    // Si el pago es via transferencia, simularemos que solamente necesitamos la cuenta del supermercado
    // la cual seria la cuenta a recibir el dinero y tambien simularemos el resultado de la transferencia
    // Esta cuenta supondriamos que es secreta

    // Aca se estaria procesando todo aquello critico a nivel de seguridad.
    println!("Realizando las conexiones y transferencias con el banco");
    println!("Monto a pagar: {}", monto_a_pagar);
    println!("Cuenta Bancaria Secreta (o no tanto jejeej): 123456789-0");

    //Ahora simulamos el resultado
    modelo_pagos::ResultadoPago {
        metodo_pago: String::from("Transferencia Bancaria"),
        fue_exitoso: true,
        cambio: 0.0
    }
}

        
        
    

En el bloque de código anterior, lo que es importante notar es la forma en la cual utilizamos un módulo dentro de otro.
use crate::models::modelo_pagos;

La línea anterior funciona porque agregamos el módulo dentro del root crate (main.rs) y la forma de interpretarla es, "usa el módulo llamado modelo_pagos, que se encuentra dentro del crate root y a su vez dentro de la carpeta models".

Ahora vamos a agregar las funcionalidades de compra (siempre de forma simulada) dentro de src/operaciones/compras.rs

        
        
            // src/operaciones/compras.rs

use crate::models::modelo_compras::Item;
use crate::models::modelo_pagos;

use crate::operaciones::pagos;

pub fn agregar_item(items_compra: &mut Vec<Item>, item: Item) {
    // Agrega un item a un vector con todos los items de la compra
    items_compra.push(item);
}

pub fn quitar_item(items_compra: &mut Vec<Item>, indice: usize) {
    // Quitara un item del array a partir de un indice
    items_compra.remove(indice);
}

pub fn mostrar_items(items_compra: &Vec<Item>) {
    // Mostrando los items y el indice
    for (index, item) in items_compra.iter().enumerate() {
        let subtotal = item.cantidad * item.precio_unitario;
        println!("[{}]. {} - Cantidad: {} - Precio U: ${} - Subtotal: ${}", index, item.nombre, item.cantidad, item.precio_unitario, subtotal);
    }
}

pub fn total_compra(items_compra: &Vec<Item>) -> f32 {
    // Devolvera el totla a pagar de todos los items de la compra
    let mut total_compra: f32 = 0.0;
    for item in items_compra {
        total_compra = total_compra + (item.cantidad * item.precio_unitario);
    }

    // redondeando a dos decimales
    let y = 10i32.pow(2) as f32;
    total_compra = (total_compra * y).round() / y;

    total_compra
}

pub fn pagar_compra(metodo_de_pago: modelo_pagos::MetodoDePago, monto_a_pagar: f32, recibido_del_cliente: f32, tarjeta: &str) -> modelo_pagos::ResultadoPago{
    // El parametro metodo_de_pago es la forma de pago elegida por el cliente.
    // El parametro monto_a_pagar es el total a pagar de la compra.
    // recibido_del_cliente es la cantidad de dinero recibida del cliente, si no es efectivo, es igual al monto a pagar
    // tarjeta, es el numero de tarjeta del cliente, si el pago es en efectivo o por transferencia, no es necesario, puede ser cualquiera

    // Ahora, dependiendo del metodo de pago elegido por el cliente, invocamos las funciones privadas, esto puede hacerse
    // porque estan dentro del mismo alcance de este metodo.

    let resultado = pagos::pagar(metodo_de_pago, monto_a_pagar, recibido_del_cliente, tarjeta);

    resultado
}

        
        
    

En este ejemplo estamos utilizando el módulo de pagos, el cual se encuentra en el mismo folder, sin embargo, lo estamos importando siempre utilizado crate, también es de notar que solamente en este ejemplo, estamos importando la estructura "Item" directamente, recuerda que también esta es otra forma de hacerlo igualmente válida 😄

Y ahora actualizamos nuestro archivo src/operaciones/mod.rs

        
        
            // src/operaciones/mod.rs

pub mod compras;
pub mod pagos;

        
        
    

Recuerda que también debemos actualizar nuestro main.rs para hacerle saber a nuestro crate de la existencia del módulo "operaciones"

        
        
            // src/main.rs

mod models;
mod operaciones;

use models::modelo_compras;

fn main() {

    let mut items_compra: Vec<modelo_compras::Item> = Vec::new();
    println!("Hello, world!");
}

        
        
    

En el post anterior, leíamos los inputs desde terminal directamente en nuestras funciones, pero como ahora estamos organizando nuestro código de mejor forma, vamos a crear unos módulo para contener funciones y leer los inputs reutilizando funciones, crearemos un archivo dentro de src/utileria que llamaremos lectura_consola.rs y de una vez incluiremos un mod.rs.

Ahora dentro de src/utileria/lectura_consola.rs vamos a agregar nuestras funciones de auxilio para leer inputs desde nuestra terminal.

        
        
            // src/utileria/lectura_consola.rs

use std::io::stdin;

pub fn leer_usize() -> usize {
    let mut input: String = String::new();
    stdin().read_line(&mut input).unwrap();
    // limpiando el input de la terminal
    let input_limpio = input.replace("\n", "").replace("\r", "").parse::<usize>().unwrap();
    input_limpio
}

pub fn leer_f32() -> f32 {
    let mut input: String = String::new();
    stdin().read_line(&mut input).unwrap();
    // limpiando el input de la terminal
    let input_limpio = input.replace("\n", "").replace("\r", "").parse::<f32>().unwrap();
    input_limpio
}

pub fn leer_string() -> String {
    let mut input: String = String::new();
    stdin().read_line(&mut input).unwrap();
    input = input.replace("\n", "").replace("\r", "");
    input
}

        
        
    

y dentro de src/utileria/mod.rs

        
        
            // src/utileria/mod.rs

pub mod lectura_consola;

        
        
    

Y por último le hacemos saber a main.rs que existe el módulo de utileria.

        
        
            // src/main.rs

mod models;
mod operaciones;
mod utileria;

use models::modelo_compras;

fn main() {

    let mut items_compra: Vec<modelo_compras::Item> = Vec::new();
    println!("Hello, world!");
}

        
        
    

Ahora vamos trabajar la parte del proyecto que a mi juicio es quizá la más interesante, dentro de nuestra estructura tenemos una carpeta (módulo) con una subcarpeta (sub-módulo) el cual está destinado para el manejo del menú o todo lo que tenga que ver con el procesamiento de los inputs que leeremos desde la terminal.

Esta sección nos ayudará a ver que podemos utilizar módulos o funciones de niveles superiores (en la estructura de carpeta) dentro de sub-módulos.

Sub-Módulos con Rust.

Vamos a crear nuestro primeros sub-módulos, en el post anterior vimos que podemos tener distintos casos en las cuales nuestros usuarios interactúan con las opciones de nuestro menú o con algún flujo de trabajo extra, como por ejemplo agregar un item a la compra o procesar un pago con tarjeta de crédito en la cual hay que digitar el número de la tarjeta.

Vamos a crear un sub-módulo para cada caso, dentro de src/menu/manejadores vamos a crear tres archivos:

menu_compras.rs: el cual manejará cualquier opción sobre agregar, quitar items, mostrar el total de compra, etc. Recuerda que esto, por facilidad, lo haremos de forma simuladas y sin validaciones.

menu_pagos.rs: el cual manejará todas las interacciones para realizar un pago (de forma simulada y sin validaciones)

menu_principal.rs: El cual contendrá la función para mostrar en pantalla la función principal.

y también incluiremos nuestro mod.rs, debería de verse así:

Ahora en el archivo src/menu/manejadores/menu_compras.rs vamos a agregar el siguiente código:

        
        
            // src/menu/manejadores/menu_compras.rs

use crate::models::modelo_compras::Item;
use crate::operaciones::compras;
use crate::utileria::lectura_consola;

pub fn manejar_agregar_item(items_compra: &mut Vec<Item>) {
    // Solicitando que el usuario registre los datos del item
    // por facilidad no hemos colocado validaciones, en un ambiente
    // laboral SI debes colocarlas.
    // Quiza no es el input mas optimo del planeta pero sirve a manera
    // de ejemplo de uso de modulos
    println!("Escribe los detalles del Item");

    println!("NOMBRE:");
    let nombre = lectura_consola::leer_string();

    println!("CANTIDAD:");
    let cantidad = lectura_consola::leer_f32();

    println!("PRECIO UNITARIO: ");
    let precio_unitario = lectura_consola::leer_f32();

    // Creando el item con la estructura que importamos de nuestro modulo
    let item: Item = Item {
        nombre,
        precio_unitario,
        cantidad
    };

    // Agregando el item a la compra
    compras::agregar_item(items_compra, item);

    println!("Item Agregado!");
}

pub fn manejar_quitar_item(items_compra: &mut Vec<Item>) {

    // mostrando los items par que el usuario pueda saber cual quitar
    // REUTILIZANDO NUESTRO MODULO !!!
    println!("Selecciona el indice que quieres quitar");
    compras::mostrar_items(items_compra);

    // Obteniendo el itema a eliminar
    let indice = lectura_consola::leer_usize();

    // Eliminando el item utilizando la funcion dentro del modulo de compra
    compras::quitar_item(items_compra, indice);
    println!("Item eliminado");
}

        
        
    

Es importante ver que en las primeras tres líneas:

use crate::models::modelo_compras::Item;

use crate::operaciones::compras;

use crate::utileria::lectura_consola;

Estamos importando funciones de módulos en un nivel superior en la estructura de archivos, lo cual es bastante útil.

Vamos ahora a completar el código para los otros archivos:

        
        
            // src/menu/manejadores/menu_pagos.rs

use crate::models::modelo_compras;
use crate::models::modelo_pagos;

use crate::operaciones::compras;
use crate::utileria::lectura_consola;

pub fn manejar_realizar_pago(items_compra: &mut Vec<modelo_compras::Item>) {

    let monto_a_pagar = compras::total_compra(items_compra);

    println!("Monto a Pagar: ${}", monto_a_pagar);
    println!("Selecciona el metodo de pago.");
    println!("1. En Efectivo");
    println!("2. Tarjeta");
    println!("3. Transferencia Bancaria");

    let mut recibido_del_cliente = 0.0;
    let mut tarjeta = String::from("N/A");

    // Obtenemos la opcion que selecciona el usuario
    let opcion_seleccionada = lectura_consola::leer_usize();

    let metodo_de_pago = match opcion_seleccionada {
        1 => modelo_pagos::MetodoDePago::Efectivo,
        2 => modelo_pagos::MetodoDePago::Tarjeta,
        3 => modelo_pagos::MetodoDePago::TransferenciaBancaria,
        _ => modelo_pagos::MetodoDePago::Efectivo // por facilidad
    };

    if opcion_seleccionada == 1 {
        // El metodo de pago es efectivo, necesitamos saber cuanto recibimos del cliente
        println!("Monto Recibido del Cliente:");
        recibido_del_cliente = lectura_consola::leer_f32();
    }

    if opcion_seleccionada == 2 {
        println!("Num. De Tarjeta:");
        // El metodo de pago es con tarjeta, necesitamos saber el numero
        tarjeta = lectura_consola::leer_string();
    }

    let resultado_del_pago: modelo_pagos::ResultadoPago = compras::pagar_compra(metodo_de_pago, monto_a_pagar, recibido_del_cliente, &tarjeta);

    if resultado_del_pago.fue_exitoso {
        println!("El pago fue exitoso");
        println!("Metodo de Pago: {}", resultado_del_pago.metodo_pago);
        println!("Cambio: ${}", resultado_del_pago.cambio);
    } else {
        println!("Hubo un problema procesando el pago, intentalo de nuevo");
    }
}

        
        
    
        
        
            // src/menu/manejadores/menu_principal.rs

pub fn mostrar_menu() {
    println!("OPCIONES:");
    println!("1. Agregar Item");
    println!("2. Quitar Item");
    println!("3. Mostrar Items");
    println!("4. Total a Pagar");
    println!("5. Realizar Pago");
    println!("6. Cancelar Compra y Salir");
    println!("Selecciona una opcion");
}

        
        
    
        
        
            // src/menu/manejadores/mod.rs

pub mod menu_compras;
pub mod menu_pagos;
pub mod menu_principal;

        
        
    

Es importante notar que aunque sean sub-módulos, también agregamos el archivo mod.rs.

Ahora vamos a crear un módulo que gestionará todas las acciones de nuestro usuario y que utilizará nuestros sub-módulos.

Dentro de src/menu crearemos dos archivos, creacion_menu.rs que contendrá nuestras funcionalidades principales y mod.rs. Debería de verse así.

Ahora vamos a agregar las funcionalidades dentro de src/menu/creacion_menu.rs

        
        
            // src/menu/creacion_menu.rs

use crate::models::modelo_compras;
use crate::menu::manejadores::{menu_principal, menu_pagos};


// Una forma interesante de hacer el import:
use super::manejadores::menu_compras; // tambien vale crate::menu::manejadores::menu_compras;
use crate::operaciones::compras;

use crate::utileria::lectura_consola;

pub fn menu_principal(items_compra: &mut Vec<modelo_compras::Item>) {
    // Iniciamos un loop en el cual vamos a preguntar al usuario la accion a realizar
    // Dependiendo de sus seeleccion vamos a realizar una tarea, todas ellas dependeran
    // de funciones dentro del modulo "compra"
    loop {
        menu_principal::mostrar_menu();

        // Obtenemos la opcion que selecciona el usuario
        // limpiando el input de la terminal
        let opcion_seleccionada = lectura_consola::leer_usize();

        match opcion_seleccionada {
            1 => menu_compras::manejar_agregar_item(items_compra), // Agregar un item
            2 => menu_compras::manejar_quitar_item(items_compra), // quitar un item
            3 => compras::mostrar_items(&items_compra), // mostrar todos los items y sus indices
            4 => println!("Total a pagar: ${}", compras::total_compra(&items_compra)), // mostrando el total a pagar
            5 => menu_pagos::manejar_realizar_pago(items_compra), // realizar el pago
            6 => break, // terminando el programa
            _ => println!("Opcion Invalida") // la opcion no es valida, el programa continua
        };
    }
}

        
        
    

En el archivo anterior, una línea bastante interesante es:

use super::manejadores::menu_compras;

Esa es otra forma de hacer las importaciones, super, hace referencia al módulo superior, que en esta caso es el root crate, también hubiera servido colocar en lugar de esta línea:

crate::menu::manejadores::menu_compras;

También puedes ver las líneas donde se importan los sub-módulos, en esta caso lo ponemos en una sola línea.

use crate::menu::manejadores::{menu_principal, menu_pagos};

Aunque también se podría separar en dos líneas sin problemas como:

use crate::menu::manejadores::menu_principal;

use crate::menu::manejadores::menu_pagos;

Ahora actualizamos nuestro src/menu/mod.rs

        
        
            // src/menu/mod.rs

pub mod creacion_menu;
pub mod manejadores;

        
        
    

Observa que la última línea importa al sub-módulo.

Ahora lo último que hace falta es enlazar todo en nuestro crate root (main.rs)

        
        
            // src/main.rs

mod models;
mod menu;
mod operaciones;
mod utileria;

use models::modelo_compras;
use menu::creacion_menu;

fn main() {
    // Creamos un vector para llevar el registro de los items de la compra
    let mut items_compra: Vec<modelo_compras::Item> = Vec::new();
    creacion_menu::menu_principal(&mut items_compra);

    println!("Programa Finalizado");
}

        
        
    

Si todo está bien, al ejecutar nuestro programa con:

cargo run

El programa debe correr tal cual lo vimos en el post anterior.

Puedes ver el código completo en el repositorio en github dando click acá.

Como puedes ver, organizar el código en Rust es bastante sencillo y muy intuitivo, si venimos del mundo de javascript, existe un archivo mod.rs el cual es bastante similar a un index.js.

Algo que no hemos cubierto en el ejemplo, porque prácticamente se hace de la misma manera, es que también los crates librerías se pueden organizar en varios archivos y dentro del archivo lib.rs es donde haremos nuestros pub mod <nombre del módulo>

Si quieres ver que es un crate librería puedes verlo en:

[Parte 1] - Organizar Código En Rust - Paquetes y Librerías.

Con este post finalizamos la mini-serie sobre organización de código en Rust.

Si este post te ha sido de utilidad compártelo con tus amigos y en tus redes sociales, no olvides dejar tus comentarios 😁. También puedes seguirme en twitter.

println!("Hasta la próxima!!");


 Utilizamos cookies propias y de terceros para mejorar tu experiencia, mostrar publicidad y análisis de navegación, puedes encontrar el detalle en nuestra Política de Cookies