diff --git a/diaries/rust/07 - Management.adoc b/diaries/rust/07 - Management.adoc new file mode 100644 index 0000000..c254aa8 --- /dev/null +++ b/diaries/rust/07 - Management.adoc @@ -0,0 +1,161 @@ += How to: Projektmanagement + +https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html[Link zum Buch] + +== Packages, Crates, Modules, was? + +Rust hat ein sehr hierachisches Konzept, was die Strukturierung von Projekten angeht. + +Fangen wir mal von oben an: + +=== Packages + +Packages bestehen aus Crates. +Sie fassen diese also quasi zusammen und in `Cargo.toml` wird definiert, wie die Crates zu bauen sind. + +Jedes Package, das wir bis jetzt erstellt haben, hatte standardmäßig eine "binary create" (dazu gleich mehr) im generierten Projekt. + +Die Crates können (soweit wie ich das verstanden habe) in beliebigen Ordnern existieren, falls die Crate so heißen soll wie das Package, ist der Standardpfad `src/main.rs` (für binary) bzw. `src/lib.rs` (für library). + +==== Warum mehrere Crates in einem Projekt? + +Einfaches Beispiel: Man hat eine library crate, die Funktionen für einen Webserver bereitstellt. +Man kann dann einfach eine binary crate hinzufügen, die eine Referenz-Nutzung abbildet, also direkt ein Beispiel ist. +Dies hilft Nutzern direkt und gleichzeitig testet es direkt auch (wobei richtige Tests natürlich anders zu implementieren sind). + +=== Crates + +Creates sind die eigentlichen "Module". +Es gibt zwei Arten: binary und library. + +==== Binary Crates + +Diese Crates können zu einer ausführbaren Datei kompiliert werden. +Jedes der bisherigen Beispiele, z.B. auch das link:#/diary/rust/3[Higher-Lower-Spiel] sind eine solche binary crate. + +Ihr Merkmal ist vor allem, dass eine `main`-Funktion existiert, die der Einstiegspunkt ist. + +==== Library Crate + +Wie der Name schon sagt, stellt diese Art Crate nur Funktionen zur verfügung wie eine Bibliothek. + +=== Modules + +Innerhalb einer Crate können Module existieren. +Und hier ist auch schon wieder von OOP abgeschaut. +Es können nämlich Rusts `private` und `public` hier genutzt werden. + +Im Hauptprogramm kann mit `mod modulname;` das Modul eingebunden werden. Gesucht wird das Modul dann in `./modulname.rs` oder in `./modulname/mod.rs`, wobei letzteres aber aussieht, als wäre es die veraltete Version. + +Zusätzlich kann auch direkt inline ein Modul erstellt werden. +Ein Beispiel: + +[source.notCompiling, Rust] +---- +mod testmodul { + mod nested_modul { + fn funktion() { + funktion2(); + } + fn funktion2() { + println!("Hello World"); + } + } + + mod zweites_modul { + fn funktion() {} + } +} + +fn main() { + // Hello world! Geht nicht... + crate::testmodul::nested_modul::funktion(); +} +---- + +Das funktioniert noch *nicht*. +Denn standardmäßig ist alles private, was nicht explizit public ist. +Damit wir den obigen Aufruf machen können, muss der Code so aussehen: + +[source, Rust] +---- +mod testmodul { + pub mod nested_modul { + pub fn funktion() { + funktion2(); + } + fn funktion2() { + println!("Hello World"); + } + } + + mod zweites_modul { + fn funktion() {} + } +} + +fn main() { + // Hello world! + crate::testmodul::nested_modul::funktion(); +} +---- + +Nur so kann auf Submodule und Funktionen dieser Module zugegriffen werden. +Wie im "normalen" OOP, können aus diesen öffentlichen Funktionen aber dann auch private aufgerufen werden. + +==== Von unten nach oben + +Um aus einem inneren Modul auf das äußere zuzugreifen, kann übrigens `super::...` verwendet werden. + +==== Structs und Enums + +In Modulen können natürlich auch Structs und Enums verwendet werden. + +Bei Structs ist die Besonderheit, dass die einzelnen Attribute auch wieder private oder public sein können. +So kann man folgendes machen: + +[source, Rust] +---- +mod testmodul { + pub struct Teststruct { + pub oeffentlich: String, + privat: String, + } + + impl Teststruct { + pub fn generator(wert: &str) -> Teststruct { + Teststruct { + oeffentlich: String::from(wert), + privat: String::from("Sehr geheimer Wert"), + } + } + } +} + +fn main() { + let a = crate::testmodul::Teststruct::generator("Irgendein Wert"); + // Geht + println!("Öffentlich: {}", a.oeffentlich); + // Geht nicht! + // println!("Privat: {}", a.privat); +} +---- + +Dagegen gilt für Enums: Wenn der Enum public ist, sind auch alle Varianten public. + +==== Abürzungen mit `use` + +Angenommen, wir haben eine Mediathek mit Filmen, Serien, Spielen, etc. und brauchen immer lange Zugriffspfade (also z.B. `crate::medien::spiele::liste::add()`), obwohl wir nur Spiele brauchen, kann `use` benutzt werden. + +Wenn wir also `use crate::medien::spiele;` in unseren Code einfügen, können alle diese Befehle verkürzt werden auf eben z.B. `spiele::liste::add()`. +Theoretisch können wir das bis hin zu einzelnen Funktionsnamen machen, `se crate::medien::spiele::liste:add;`, würde `add()` im Scope verfügbar machen. + +Dabei gibt es zwei Hinweise: +1. Es funktioniert nur, wenn sich zwei Namespaces nicht überschneiden. Ein Zufügen von `use andere::mod::add;` geht also nicht! +2. Das ganze gilt nur in genau diesem Scope. Falls wir jetzt ein weiteres Modul definieren, können wir darin nicht die Pfade kürzen. + +Und für beides gibt es Umwege: +1. Man kann `use andere::mod::add as modAdd;` benutzen. +2. Sollten wir `pub use ...` benutzen, kann tatsächlich diese Abkürzung benutzt werden. + +`pub use` kann auch benutzt werden, alle möglichen Module in seiner Crate miteinander reden zu lassen, aber nach außen nur bestimmte Schnittstellen freizugeben. diff --git a/list.json b/list.json index 9a39796..69be3d4 100644 --- a/list.json +++ b/list.json @@ -42,7 +42,8 @@ { "title": "03 - Basics", "filename": "03 - Concepts"}, { "title": "04 - Ownership", "filename": "04 - Ownership"}, { "title": "05 - Structs", "filename": "05 - Structs"}, - { "title": "06 - Enums", "filename": "06 - Enums"} + { "title": "06 - Enums", "filename": "06 - Enums"}, + { "title": "07 - Crates & Modules", "filename": "07 - Management"} ] }, {