diff --git a/diaries/rust/06 - Enums.adoc b/diaries/rust/06 - Enums.adoc new file mode 100644 index 0000000..44dd4c8 --- /dev/null +++ b/diaries/rust/06 - Enums.adoc @@ -0,0 +1,152 @@ +:experimental: +:docdatetime: 2022-07-20T14:07:20+02:00 + += Enums und Pattern Matching + +https://doc.rust-lang.org/book/ch06-00-enums.html[Link zum Buch] + +== Enums + +Enumarations gibt's in vielen Programmiersprachen, in Rust scheinen sie aber eine große Rolle einzunehmen. +"Enumeration" stimmt eigentlich gar nicht, Enums haben hier nämlich nicht zwangsläufig was mit Zahlen zu tun. +Grundsätzlich ist ein "Enum" in Rust näher am "Union" würde ich denken. + +Ein einfaches Beispiel für ist der Typ `Option` (vergleichbar mit Python oder Java `Optional`). +Dieser ist entweder `None` oder `Some(value: T)` - es kann also ein Wert zusätzlich zur "Definition" beinhalten. + +[source, Rust] +---- +enum Farbcode { + Hex, + Rgb, +} + +let hexcolor = Farbcode::Hex; +---- + +`Farbcode` ist also ein im Code benutzbarer Datentyp, genauso wie `Farbcode::Hex`. +Wenn eine Funktion nun eine Variable mit Typ `Farbcode` erwartet, kann diese Variable sowohl `Hex` oder `Rgb` sein. +Die Funktion kann dann je nach Typ verschieden funktionieren. + +Wie schon erwähnt, kann so ein Enum-Wert auch Werte beinhalten, um das zu machen, schreiben wir den Code einfach um: + +[source, Rust] +---- +enum Farbcode { + Hex(String), + Rgb(u8, u8, u8), +} + +// Alternativ: +struct Hex(String); +struct Rgb(u8, u8, u8); + +enum Farbcode { + Hex, + Rgb +} + +let hexcode = Farbcode::Hex(String::from("00affe")); +let rgbcode = Farbcode::Rgb(125, 255, 255); +---- + +Natürlich können die Structs jeder Art sein. +Enums sind aber auch selber eine Art Struct. +Also können wir für Enums auch Methoden definieren wie für Structs. + +[source, Rust] +---- +impl Farbcode { + fn to_css_string(&self) { + // Methode, die für Hex und Rgb angewendet werden kann + } +} + +let rgbcode = Farbcode::Rgb(125, 255, 255); +rgbcode.to_css_string(); +---- + +Tatsächlich ist damit so etwas wie Vererbung implementierbar. +Es gibt zwar keine Attribute, aber da ja auch die internen Structs Methoden haben können, ist eine gewisse Hierarchie erstellbar. + +=== `Option` + +Options hab ich oben schonmal kurz beschrieben. +In Rust ist dieser Datentyp sehr wichtig. +Die Dokumentation dazu ist https://doc.rust-lang.org/std/option/enum.Option.html[hier zu finden] und enthält sehr viel Wichtiges und Interesantes. + +== `match` + +`match` ist quasi das `switch` von Rust. +Nur kann es auch prüfen, ob eine Variable einem Enum-Typen angehört. +So wie Rust bis jetzt klang, kann wahrscheinlich jedem Datentypen ein "match-Trait" gegeben werden, der dann eine "Zugehörigkeit" (Gleicheit stimmt ja irgendwie nicht) prüfen kann. + +Aber ganz einfach: Angenommen wir wollen die Methode `toCSSString` von oben implementieren. +Diese Methode muss ja, je nach Typ, völlig unterschiedlich funktionieren. + +[source, Rust] +---- +enum Farbcode { + Hex(String), + Rgb(u8, u8, u8), +} + +impl Farbcode { + fn to_css_string(&self) -> String { + match self { + // format! ist offensichtlich ein Pragma, dass Strings erstellt auf die selbe Weise wie println! + Farbcode::Hex(hex) => format!("#{}", hex), + Farbcode::Rgb(r, g, b) => format!("rgb({}, {}, {})", r, g, b), + } + } +} + +fn main() { + let hexcode = Farbcode::Hex(String::from("affe00")); + let rgbcode = Farbcode::Rgb(125, 255, 255); + + println!("{}", hexcode.to_css_string()); + println!("{}", rgbcode.to_css_string()); +} +---- + +Hier sieht man auch ganz gut, wie im Match dem "Inhalt" des Enums direkt Namen gegeben werden und Tuples auch dekonstruiert. +Im Beispiel ist auch deutlich, dass `match` einen Rückgabewert hat, nämlich das, was im Statement(-Block) des jeweiligen Matches zurückgegeben wird. + +=== Vollständigkeit + +Entweder muss ein `match` eines Enums jede mögliche Variante abgrasen oder es gibt zwei Alternativen. + +`other` ist quasi das `default` von Rust. +Aber auch `_` matched alles. +Der Unterschied ist, dass bei `other` noch der Inhalt genutzt werden kann, bei `_` wird er direkt ignoriert und ist nicht nutzbar. + +=== `if let` + +Dieses if-Konstrukt nutzt man am besten, wenn man nur auf eine einzelne Variante eines Enums prüfen möchte. +Letzendlich ist es ganz simpel: + +[source, Rust] +---- +#[derive(Debug)] +enum Muenzwurf { + Kopf, + Zahl, + Seite +} + +fn print_wurf(ergebnis: Muenzwurf) { + if let Muenzwurf::Seite = ergebnis { + println!("Das glaub ich nicht! Seite?!"); + } else { + println!("Du hast {:?} geworfen.", ergebnis); + } +} + +fn main() { + let ergebnis = Muenzwurf::Zahl; + print_wurf(ergebnis); // Du hast Zahl geworfen. + let ergebnis = Muenzwurf::Seite; + print_wurf(ergebnis); // Das glaub ich nicht! Seite?! +} +----