Wie man ein umfangreiches Rust-Projekt effektiv organisiert
Wenhao Wang
Dev Intern · Leapcell

Rust Projektstruktur
Viele Lernende sind verwirrt, wenn sie Rust lernen, und fragen sich, ob ihre Projektdateistruktur korrekt und standardgemäß ist. In diesem Artikel werden wir untersuchen, wie ein großes Rust-Projekt seinen Code organisiert, beginnend mit den einfachsten main.rs
und lib.rs
.
Crate
- Eine Crate ist Rusts grundlegende Kompilierungseinheit. Jede Crate ist ein unabhängiges Kompilierungsziel und kann entweder eine Bibliothek (lib crate) oder eine ausführbare Datei (binary crate) sein.
- Eine Crate hat eine Root-Datei: Für eine Bibliotheks-Crate ist es
src/lib.rs
; für eine binäre Crate ist essrc/main.rs
.
Package
Ein einfaches Rust-Projekt kann jedoch nicht nur aus diesen beiden Dateien bestehen.
Ein Package ist eine Sammlung von einer oder mehreren Crates. Es enthält die Dateien Cargo.toml
und Cargo.lock
, die die Metadaten und Abhängigkeiten des Packages definieren.
In einem tatsächlichen Projekt enthält eine Crate nur Code und Module, während die Dateien Cargo.toml
und Cargo.lock
Teil des Packages sind und für die Verwaltung und das Erstellen des gesamten Packages verantwortlich sind.
Wenn wir beispielsweise cargo new sdk
verwenden, um eine Bibliothek zu erstellen, sieht die resultierende Struktur wie folgt aus:
Beispiel für eine Dateistruktur
// Library crate
sdk/
├── Cargo.toml
├── Cargo.lock
└── src
└── lib.rs
or
// Binary crate
sdk/
├── Cargo.toml
├── Cargo.lock
└── src
└── main.rs
TOML-Datei
Die TOML
-Datei wird verwendet, um Abhängigkeiten und Versionsinformationen zu verwalten, zum Beispiel:
[package] name = "sdk" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies]
Hinzufügen eines häufig verwendeten Packages zur Vereinfachung der Fehlerbehandlung: thiserror
. Sie können den Befehl cargo add thiserror
verwenden:
[package] name = "sdk" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] thiserror = "1.0.61"
Testcode und Performance-Tests
An diesem Punkt schreiben wir bereits ein komplettes Projekt. Ein wesentlicher Bestandteil jedes Projekts ist jedoch das Testen, einschließlich Unit-Tests und Performance-Tests. Wo sollten diese Testdateien platziert werden? Verwenden wir weiterhin unser sdk
-Projekt als Beispiel.
Nach Community- und offiziellen Standards sollten Test- und Benchmark-Dateien in den Verzeichnissen tests
und benches
auf derselben Ebene wie src
platziert werden, wie unten gezeigt:
sdk/
├── Cargo.toml
├── src/
│ └── lib.rs
├── tests/
│ ├── some-integration-tests.rs
│ └── multi-file-test/
│ ├── main.rs
│ └── test_module.rs
└── benches/
├── large-input.rs
└── multi-file-bench/
├── main.rs
└── bench_module.rs
Beim anfänglichen Schreiben des Projekts können Unit-Tests direkt unter den relevanten Codedateien platziert werden, sodass keine Notwendigkeit besteht, das Verzeichnis multi-file-test
und die Dateien zu erstellen. Im Laufe der Entwicklung und wenn Testcode viel Platz einnimmt, wird jedoch empfohlen, sie in den Ordner tests
zu verschieben, um den Hauptcode sauber zu halten.
tests/
enthält funktionalen Testcode, hauptsächlich zur Überprüfung der Feature-Implementierung.benches/
enthält Performance-Testcode, hauptsächlich zur Messung der Performance (z. B. Service-API-Performance-Tests).
Workspace
Was ist, wenn ein großes Projekt aus mehreren Rust-Projekten besteht? Nehmen wir zum Beispiel an, die sdk
-Crate wird von einem Entwickler verwaltet, und wir müssen auch ein CLI-Projekt und ein Serverprojekt darauf aufbauen. Sollen wir den Code in drei separate Projekte aufteilen? Das könnte die Dinge durcheinander bringen. Gibt es eine einheitliche Möglichkeit, sie zu verwalten? Ja, mit Workspaces.
In Rust ist ein Workspace eine Möglichkeit, mehrere Packages innerhalb eines einzelnen Projekts zu organisieren und zu verwalten. Workspaces bieten Tools und Mechanismen, die das Dependency Management, das Erstellen und das Testen über mehrere zusammengehörige Packages hinweg vereinfachen.
Vorteile der Verwendung eines Workspaces
- Mehrere Packages organisieren: Workspaces ermöglichen das Gruppieren mehrerer Packages, die Library Crates, CLI-Tools oder andere Arten von Packages enthalten können.
- Gemeinsame Abhängigkeiten: Alle Packages in einem Workspace teilen sich eine einzige
Cargo.lock
-Datei, wodurch konsistente Dependency-Versionen sichergestellt und Konflikte vermieden werden. - Vereinfachter Build-Prozess: Das Ausführen von
cargo build
odercargo test
im Root-Workspace-Verzeichnis erstellt und testet rekursiv alle Workspace-Packages. - Konsistenz: Die gemeinsam genutzte
Cargo.lock
-Datei und die einheitlichen Build-Befehle stellen sicher, dass alle Packages konsistent und gut koordiniert bleiben.
Workspace-Struktur
Angenommen, wir möchten sdk
, cli
und server
im selben Workspace platzieren. Die Verzeichnisstruktur würde wie folgt aussehen:
Ein typischer Workspace besteht aus einem übergeordneten Verzeichnis, das eine Root-Cargo.toml
-Datei und mehrere Sub-Packages enthält, von denen jedes eine eigene Cargo.toml
-Datei und ein Quellverzeichnis hat.
my_workspace /
├── Cargo.lock
├── Cargo.toml
├── crates/
│ ├── sdk/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ └── lib.rs
│ │ └──── tests/
│ │ ├── some-integration-tests.rs
│ │ └── multi-file-test/
│ │ ├── main.rs
│ │ └── test_module.rs
│ ├── cli/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ └── main.rs
│ │ ├── bin/
│ │ │ ├── named-executable.rs
│ │ │ ├── another-executable.rs
│ │ │ └── multi-file-executable/
│ │ │ ├── main.rs
│ │ │ └── some_module.rs
│ │ └──── tests/
│ │ ├── some-integration-tests.rs
│ │ └── multi-file-test/
│ │ ├── main.rs
│ │ └── test_module.rs
│ └── server/
│ ├── Cargo.toml
│ ├── src/
│ │ └── main.rs
│ ├── bin/
│ │ ├── named-executable.rs
│ │ ├── another-executable.rs
│ │ └── multi-file-executable/
│ │ ├── main.rs
│ │ └── some_module.rs
│ ├── tests/
│ │ ├── some-integration-tests.rs
│ │ └── multi-file-test/
│ │ ├── main.rs
│ │ └── test_module.rs
│ └── benches/
│ ├── large-input.rs
│ └── multi-file-bench/
│ ├── main.rs
│ └── bench_module.rs
Workspace TOML-Datei
Spezifizieren Sie den Resolver, resolver = "2"
, um Cargos Resolver der zweiten Generation zu verwenden, der effizienter ist.
[workspace] resolver = "2"
Definieren Sie Workspace-Package-Informationen:
[workspace.package] name = "my-workspace" version = "0.1.0" edition = "2021"
Fügen Sie Workspace-Mitglieder hinzu:
[workspace] members = [ "crates/sdk", "crates/cli", "crates/server", ]
Spezifizieren Sie Abhängigkeiten, die von Workspace-Mitgliedern gemeinsam genutzt werden:
[workspace.dependencies] thiserror = "1.0.61"
Sie fragen sich vielleicht: „Aber das habe ich bereits in der Cargo.toml
von sdk
definiert. Warum definiere ich es erneut im Workspace?“
Dies liegt daran, dass der Workspace die Zentralisierung des Dependency Managements ermöglicht. Wenn sowohl cli
als auch server
auch thiserror
benötigen, sollten wir thiserror = "1.0.61"
separat in jeder ihrer Cargo.toml
-Dateien definieren? Das könnten wir tun, aber dies führt zu potenziellen Problemen: Wenn verschiedene Versionen über Projekte hinweg verwendet werden, kann die Kompilierung langsamer sein und die kompilierten Binärdateien können redundante Kopien von thiserror
enthalten.
Um die Kompilierzeit und die Binärdateigröße zu optimieren, legen wir eine einheitliche Dependency-Version im Workspace fest:
# In the workspace `Cargo.toml` [workspace.dependencies] thiserror = "1.0.61"
# In the `cli` and `server` packages [dependencies] thiserror.workspace = true
Cross-Package-Abhängigkeiten
Damit cli
und server
Methoden von sdk
verwenden können, müssen wir Abhängigkeiten im Workspace deklarieren:
[workspace.dependencies] sdk = { path = "crates/sdk" } cli = { path = "crates/cli" } server = { path = "crates/server" }
Dann in den Package-spezifischen Cargo.toml
-Dateien:
[dependencies] sdk.workspace = true thiserror.workspace = true
Dies stellt sicher, dass alle Projekte im Workspace auf sdk
verweisen können, ohne doppelte Dependency-Versionen.
Wir sind Leapcell, Ihre erste Wahl für das Hosting von Rust-Projekten.
Leapcell ist die Serverless-Plattform der nächsten Generation für Webhosting, asynchrone Aufgaben und Redis:
Multi-Language-Unterstützung
- Entwickeln Sie mit Node.js, Python, Go oder Rust.
Stellen Sie unbegrenzt Projekte kostenlos bereit
- Zahlen Sie nur für die Nutzung – keine Anfragen, keine Gebühren.
Unschlagbare Kosteneffizienz
- Pay-as-you-go ohne Leerlaufgebühren.
- Beispiel: 25 US-Dollar unterstützen 6,94 Millionen Anfragen bei einer durchschnittlichen Antwortzeit von 60 ms.
Optimierte Entwicklererfahrung
- Intuitive Benutzeroberfläche für mühelose Einrichtung.
- Vollständig automatisierte CI/CD-Pipelines und GitOps-Integration.
- Echtzeitmetriken und -protokollierung für umsetzbare Erkenntnisse.
Mühelose Skalierbarkeit und hohe Leistung
- Automatische Skalierung zur einfachen Bewältigung hoher Parallelität.
- Kein Betriebsaufwand – konzentrieren Sie sich einfach auf das Bauen.
Erfahren Sie mehr in der Dokumentation!
Folgen Sie uns auf X: @LeapcellHQ