Organice el código con módulos y crates

RustBeginner
Practicar Ahora

Introducción

A medida que crece un programa de Rust, mantener todos los tipos y funciones en un solo archivo dificulta distinguir las responsabilidades. Ya ha visto una biblioteca y un binario en el laboratorio de CLI, así como un módulo de pruebas en el laboratorio de testing. Este laboratorio conecta ahora esos ejemplos preparados en un único modelo: un paquete es el proyecto que compila Cargo, los crates son unidades de compilación y los módulos organizan los nombres dentro de un crate.

Organizará un paquete pequeño para generar informes de una despensa. Los archivos y la mayor parte del código de implementación ya están preparados, de modo que cada paso se centra en un límite concreto: compartir elementos desde el crate de biblioteca, declarar un módulo en un archivo e importar una pequeña API pública en el crate binario.

Comparta una función entre crates

En este paso, identificará los dos crates del paquete y hará que una función de la biblioteca esté disponible para el binario.

Entre en el paquete de Cargo preparado:

cd /home/labex/project/pantry-report

Cargo.toml describe un paquete llamado pantry-report. Cargo reconoce src/lib.rs como la raíz de un crate de biblioteca y src/main.rs como la raíz de un crate binario. Ambos pertenecen al mismo paquete, pero se compilan como crates independientes.

Revise las raíces breves de los crates. Los comandos sed -n muestran los rangos de líneas solicitados sin modificar los archivos:

sed -n '1,120p' src/lib.rs
sed -n '1,120p' src/main.rs

El binario importa report_title mediante la ruta del crate de biblioteca. En el código fuente de Rust, un nombre de paquete que contiene un guion se convierte en un nombre de crate con un guion bajo, por lo que pantry-report se convierte en pantry_report.

El tipo de retorno preparado &'static str describe un literal de cadena prestado que sigue siendo válido durante todo el programa. El marcador 'static es una anotación de tiempo de vida. Las reglas explícitas de tiempo de vida quedan fuera de este curso para principiantes; ningún cambio que usted haga en este laboratorio depende de entenderlo o escribirlo.

Los elementos son privados para su módulo a menos que se marquen con pub. Abra la raíz de la biblioteca:

nano src/lib.rs

Cambie únicamente la declaración de la función, de:

fn report_title() -> &'static str {

a:

pub fn report_title() -> &'static str {

pub convierte la función en parte de la interfaz pública del crate de biblioteca. Guarde con Ctrl+O, pulse Enter y salga con Ctrl+X.

Use cargo check para comprobar los tipos de ambos crates sin generar una compilación ejecutable final:

cargo check

Después, ejecute el crate binario:

cargo run --quiet

La salida es:

Pantry Report

Esto demuestra que el binario puede atravesar el límite entre crates y llamar a la función pública de la biblioteca.

Declare un módulo en un archivo

En este paso, conectará el archivo preparado inventory.rs con el árbol de módulos del crate de biblioteca.

Un archivo de código fuente de Rust no se compila simplemente porque exista. La raíz del crate debe declarar el módulo. Para una declaración llamada inventory, Rust busca src/inventory.rs y coloca sus elementos bajo la ruta inventory::....

Revise el archivo de módulo preparado. Como antes, sed -n '1,200p' usa -n para suprimir la salida automática y 1,200p para mostrar únicamente el rango de líneas solicitado:

sed -n '1,200p' src/inventory.rs

La línea #[derive(Debug)] pide a Rust que genere compatibilidad estándar con el formato de depuración para Item. El programa visible no depende de esta característica, así que considérela metadatos preparados y no un requisito nuevo. El tipo Item, sus campos y describe son públicos. El auxiliar availability no tiene pub, por lo que permanece privado dentro del módulo. describe puede llamar a ese auxiliar privado, mientras que los llamadores externos reciben únicamente el resultado público.

Abra la raíz de la biblioteca:

nano src/lib.rs

Reemplace // MODULE_DECLARATION por:

pub mod inventory;

El primer pub expone el módulo a través del crate de biblioteca. La parte mod inventory; incorpora el archivo al árbol de módulos. Guarde y salga de Nano; después, compruebe el paquete:

cargo check
Finished `dev` profile ...

La comprobación correcta demuestra que Rust encontró src/inventory.rs y lo compiló como pantry_report::inventory.

Importe y use la API pública

En este paso, usará una declaración use para incorporar elementos del módulo al crate binario e imprimir un registro de la despensa.

Sin una importación, las rutas completas son pantry_report::inventory::Item y pantry_report::inventory::describe. Una declaración use crea nombres más cortos en el ámbito actual sin mover ni copiar los elementos.

Abra el código fuente del binario:

nano src/main.rs

Reemplace // INVENTORY_IMPORT por esta importación agrupada:

use pantry_report::inventory::{describe, Item};

Las llaves agrupan dos elementos que comparten el mismo prefijo de ruta. Después, reemplace // INVENTORY_REPORT por:

    let lentils = Item {
        name: String::from("lentils"),
        quantity: 4,
    };
    println!("{}", describe(&lentils));

El binario puede construir Item porque el tipo y ambos campos son públicos. Presta el elemento a describe; esa función pública llama de forma privada a availability dentro de su propio módulo.

Guarde y salga de Nano; después, ejecute el paquete:

cargo run --quiet
Pantry Report
lentils: 4 jars (stocked)

Las dos líneas muestran que tanto el intercambio entre crates como la organización mediante módulos funcionan a través de una pequeña API pública.

Resumen

Ha trabajado con un paquete de Cargo que contiene crates de biblioteca y binario, ha hecho público un elemento de la biblioteca, ha declarado un módulo en un archivo, ha seguido rutas de crates y módulos, ha acortado esas rutas con use y ha mantenido privado un auxiliar de implementación detrás de una pequeña API pública.