Code mit Modulen und Crates organisieren

RustBeginner
Jetzt üben

Einführung

Wenn ein Rust-Programm wächst, wird es schwieriger, die Zuständigkeiten zu erkennen, wenn jeder Typ und jede Funktion in einer einzigen Datei steht. Im CLI-Lab haben Sie bereits eine Bibliothek und eine Binärdatei kennengelernt, im Test-Lab ein Testmodul. Dieses Lab verbindet diese vorbereiteten Beispiele zu einem gemeinsamen Modell: Ein Paket ist das Projekt, das Cargo erstellt, Crates sind Kompilationseinheiten und Module ordnen Namen innerhalb eines Crates.

Sie organisieren ein kleines Paket für einen Vorratsbericht. Die Dateien und der größte Teil des Implementierungscodes sind bereits vorbereitet. Daher konzentriert sich jeder Schritt auf eine bestimmte Grenze: Sie geben Funktionen aus dem Bibliotheks-Crate frei, deklarieren ein Dateimodul und importieren eine kleine öffentliche API in das Binär-Crate.

Eine Funktion zwischen Crates freigeben

In diesem Schritt identifizieren Sie die beiden Crates des Pakets und machen eine Bibliotheksfunktion für das Binär-Crate verfügbar.

Wechseln Sie in das vorbereitete Cargo-Paket:

cd /home/labex/project/pantry-report

Cargo.toml beschreibt ein Paket mit dem Namen pantry-report. Cargo erkennt src/lib.rs als Wurzel eines Bibliotheks-Crates und src/main.rs als Wurzel eines Binär-Crates. Beide gehören zum selben Paket, werden aber als getrennte Crates kompiliert.

Sehen Sie sich die kurzen Crate-Wurzeln an. Die Befehle mit sed -n geben die angeforderten Zeilenbereiche aus, ohne die Dateien zu bearbeiten:

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

Das Binär-Crate importiert report_title über den Pfad des Bibliotheks-Crates. Ein Paketname mit Bindestrich wird im Rust-Quellcode zu einem Crate-Namen mit Unterstrich. Daher wird pantry-report zu pantry_report.

Der vorbereitete Rückgabetyp &'static str bezeichnet ein ausgeliehenes String-Literal, das während der gesamten Programmlaufzeit gültig bleibt. Die Markierung 'static ist eine Lebensdauerannotation. Ausdrückliche Regeln zu Lebensdauern gehören nicht zu diesem Einsteigerkurs; für keine Änderung in diesem Lab müssen Sie eine solche Annotation verstehen oder schreiben.

Elemente sind in ihrem Modul privat, sofern sie nicht mit pub gekennzeichnet sind. Öffnen Sie die Bibliothekswurzel:

nano src/lib.rs

Ändern Sie nur die Funktionsdeklaration von:

fn report_title() -> &'static str {

in:

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

Mit pub wird die Funktion Teil der öffentlichen Schnittstelle des Bibliotheks-Crates. Speichern Sie mit Ctrl+O, drücken Sie Enter und beenden Sie den Editor mit Ctrl+X.

Verwenden Sie cargo check, um beide Crates auf korrekte Typen zu prüfen, ohne einen endgültigen ausführbaren Build zu erstellen:

cargo check

Führen Sie anschließend das Binär-Crate aus:

cargo run --quiet

Die Ausgabe lautet:

Pantry Report

Damit ist nachgewiesen, dass das Binär-Crate die Crate-Grenze überschreiten und die öffentliche Bibliotheksfunktion aufrufen kann.

Ein Dateimodul deklarieren

In diesem Schritt verbinden Sie die vorbereitete Datei inventory.rs mit dem Modulbaum des Bibliotheks-Crates.

Eine Rust-Quelldatei wird nicht allein deshalb kompiliert, weil sie existiert. Die Crate-Wurzel muss das Modul deklarieren. Bei einer Deklaration mit dem Namen inventory sucht Rust nach src/inventory.rs und ordnet deren Elemente dem Pfad inventory::... unter.

Sehen Sie sich die vorbereitete Moduldatei an. Wie zuvor unterdrückt sed -n '1,200p' mit -n die automatische Ausgabe. 1,200p gibt nur den angeforderten Zeilenbereich aus:

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

Die Zeile #[derive(Debug)] weist Rust an, Unterstützung für die standardmäßige Debug-Formatierung von Item zu erzeugen. Das sichtbare Programm verwendet diese Funktion nicht. Betrachten Sie sie daher als vorbereitete Metadaten und nicht als neue Anforderung. Der Typ Item, seine Felder und describe sind öffentlich. Für den Hilfsfunktion availability fehlt pub, daher bleibt sie innerhalb des Moduls privat. describe kann diese private Hilfsfunktion aufrufen, während Aufrufer außerhalb des Moduls nur das öffentliche Ergebnis erhalten.

Öffnen Sie die Bibliothekswurzel:

nano src/lib.rs

Ersetzen Sie // MODULE_DECLARATION durch:

pub mod inventory;

Das erste pub stellt das Modul über das Bibliotheks-Crate bereit. Der Teil mod inventory; fügt die Datei in den Modulbaum ein. Speichern und beenden Sie Nano. Prüfen Sie anschließend das Paket:

cargo check
Finished `dev` profile ...

Die erfolgreiche Prüfung weist nach, dass Rust src/inventory.rs gefunden und als pantry_report::inventory kompiliert hat.

Die öffentliche API importieren und verwenden

In diesem Schritt verwenden Sie eine use-Deklaration, um Modulelemente in das Binär-Crate zu holen und einen Vorratseintrag auszugeben.

Ohne Import lauten die vollständigen Pfade pantry_report::inventory::Item und pantry_report::inventory::describe. Eine use-Deklaration erzeugt kürzere Namen im aktuellen Gültigkeitsbereich, ohne die Elemente zu verschieben oder zu kopieren.

Öffnen Sie den Quellcode der Binärdatei:

nano src/main.rs

Ersetzen Sie // INVENTORY_IMPORT durch diesen gruppierten Import:

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

Geschweifte Klammern gruppieren zwei Elemente, die denselben Pfadpräfix verwenden. Ersetzen Sie anschließend // INVENTORY_REPORT durch:

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

Das Binär-Crate kann Item erzeugen, weil der Typ und beide Felder öffentlich sind. Es leiht das Element für describe aus. Diese öffentliche Funktion ruft innerhalb ihres eigenen Moduls privat availability auf.

Speichern und beenden Sie Nano. Führen Sie anschließend das Paket aus:

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

Die beiden Zeilen zeigen, dass sowohl die Freigabe auf Crate-Ebene als auch die Organisation auf Modulebene über eine kleine öffentliche API funktionieren.

Zusammenfassung

Sie haben mit einem Cargo-Paket gearbeitet, das Bibliotheks- und Binär-Crates enthält. Sie haben ein Bibliothekselement öffentlich gemacht, ein Dateimodul deklariert, Crate- und Modulpfade verwendet, diese Pfade mit use verkürzt und eine Implementierungshilfsfunktion hinter einer kleinen öffentlichen API privat gehalten.