Ein Paket, zwei Entitäten: Mythos oder Realität?
\n\nStellen Sie sich vor: Sie haben eine fantastische Bibliothek in Rust für die Arbeit mit großen Datenmengen geschrieben. Sie ist blitzschnell, sicher und elegant. Aber Sie brauchen nicht nur eine API für andere Entwickler, sondern auch ein fertiges CLI-Tool, um die Bibliothek zu testen oder in Pipelines zu verwenden. Da taucht die klassische Frage auf, die tausende Aufrufe auf Stack Overflow gesammelt hat: „Package with both a library and a binary?“ (Ein Paket, das sowohl eine Bibliothek als auch eine ausführbare Datei enthält).
\n\nFür Rust-Neulinge klingt das nach einem Rätsel. Für erfahrene Entwickler ist es eine Aufgabe zur Optimierung der Projektstruktur und Leistung. In diesem Artikel beantworten wir diese Frage nicht nur, sondern tun dies mit Fokus auf Optimierung und Performance – zwei Dinge, die Rust am meisten geliebt machen.
\n\nWir zeigen Ihnen, wie Sie ein solches Paket richtig organisieren, Code-Duplizierung vermeiden, die Kompilierung beschleunigen und Ihr Binary wirklich schnell machen. Los geht's!
\n\nPaketstruktur: Cargo.toml regelt alles
\n\nIn Rust kann ein einzelnes Cargo-Paket sowohl eine Bibliothek (lib) als auch mehrere Binaries (bin) enthalten. Das ist keine Magie, sondern eine einfache Ordnerstruktur-Konvention. Sehen wir uns ein minimales Beispiel an.
\n\nErstellen wir ein Projekt:
\n\ncargo new my_tool --lib\n\nJetzt ändern wir die Datei Cargo.toml, um explizit anzugeben, dass wir sowohl eine Bibliothek als auch ein Binary haben:
[package]\nname = "my_tool"\nversion = "0.1.0"\nedition = "2021"\n\n[lib]\nname = "my_tool_lib"\npath = "src/lib.rs"\n\n[[bin]]\nname = "my_tool"\npath = "src/main.rs"\n\n[dependencies]\n\n\nDie Ordnerstruktur sieht dann so aus:
\n\nmy_tool/\n├── Cargo.toml\n├── src/\n│ ├── lib.rs // Bibliothek\n│ └── main.rs // Binary\n\n\nGanz einfach, oder? Aber der Teufel steckt im Detail. Wenn Sie einfach den Code aus der Bibliothek in die main.rs kopieren, erhalten Sie Duplizierung und Performance-Probleme. Sie müssen klüger vorgehen.
\n\nDer richtige Ansatz: Binary nutzt die Bibliothek
\n\nDas Binary sollte die Bibliothek wie eine externe Crate importieren. Auch wenn sie sich im selben Paket befinden. Das reduziert nicht nur die Duplizierung, sondern ermöglicht dem Rust-Compiler auch eine bessere Optimierung durch Cross-Crate-Optimierung (LTO).
\n\nBeispiel src/lib.rs:
pub fn calculate(data: &[u64]) -> u64 {\n data.iter().sum()\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n fn test_calculate() {\n assert_eq!(calculate(&[1, 2, 3]), 6);\n }\n}\n\n\nBeispiel src/main.rs:
use my_tool_lib::calculate;\n\nfn main() {\n let numbers = vec![10, 20, 30];\n println!("Summe: {}", calculate(&numbers));\n}\n\n\nBeachten Sie: Wir verwenden my_tool_lib – den Namen, den wir der Bibliothek in Cargo.toml gegeben haben. Der Compiler verknüpft sie automatisch zu einem Binary, bewahrt aber die Modularität.
Performance-Optimierung: LTO, Inline und Benchmarks
\n\nNachdem die Struktur steht, sprechen wir über das Wesentliche – wie Sie das Maximum aus Ihrem Paket herausholen. Denn wenn Sie ein Tool zur Datenverarbeitung erstellen, zählt jede Mikrosekunde.
\n\n1. Link-Time Optimization (LTO) aktivieren
\n\nLTO ermöglicht es dem Compiler, Code über Crate-Grenzen hinweg zu optimieren. Ohne LTO werden Funktionen aus Bibliothek und Binary getrennt optimiert. Mit LTO – wie eine Einheit.
\n\nFügen Sie in Cargo.toml hinzu:
[profile.release]\nlto = true\ncodegen-units = 1\n\n\nDas verlängert die Kompilierzeit, macht das Binary aber deutlich schneller. Für die Bildungsseite pythonlib.ru ist es wichtig zu betonen: LTO ist der Standard für Production-Builds in Rust.
\n\n2. Inline-Hinweise nutzen
\n\nWenn Ihre Bibliothek kritische Funktionen enthält,