diff --git a/diaries/rust/00 - Hello World.adoc b/diaries/rust/00 - Hello World.adoc index 8214593..7a9160c 100644 --- a/diaries/rust/00 - Hello World.adoc +++ b/diaries/rust/00 - Hello World.adoc @@ -1,5 +1,5 @@ :experimental: -:docdatetime: 2022-06-14T12:26:06.567Z +:docdatetime: 2022-08-10T17:04:53+02:00 = Hello world @@ -12,7 +12,7 @@ spezielle Art Funktion?), die einfach auf stdout printed. ~*In[2]:*~ -[source, Rust] +[source, rust] ---- println!("Hello world!"); ---- @@ -31,7 +31,7 @@ Ein komplettes Programm zum Kompilieren hätte also den folgenden Inhalt: ~*In[3]:*~ -[source, Rust] +[source, rust] ---- fn main() { println!("Hello world!"); @@ -40,7 +40,7 @@ fn main() { Kompiliert und ausgeführt wird es dann über folgende Befehle: -[source,bash] +[source, bash] ---- $ rustc main.rs $ ./main diff --git a/diaries/rust/01 - Cargo.adoc b/diaries/rust/01 - Cargo.adoc index ea60b4d..c4d27cb 100644 --- a/diaries/rust/01 - Cargo.adoc +++ b/diaries/rust/01 - Cargo.adoc @@ -1,5 +1,5 @@ :experimental: -:docdatetime: 2022-06-14T12:26:06.567Z +:docdatetime: 2022-08-10T17:04:53+02:00 = Cargo @@ -11,7 +11,7 @@ Cargo ist Rusts package manager. + Um ein neues Cargo-Projekt zu erstellen, braucht es das folgende Command: -[source,bash] +[source, bash] ---- $ cargo new projektname --bin ---- @@ -27,7 +27,7 @@ und `Cargo.toml` erstellt. Unangetastet sieht die Datei so aus: -[source,toml] +[source, toml] ---- [package] name = "projektname" @@ -48,7 +48,7 @@ Die Main-Datei ist mit ``Hello World'' gefüllt. === cargo build -[source,bash] +[source, bash] ---- $ cargo build $ ./target/debug/projektname diff --git a/diaries/rust/02 - Higher-Lower-Spiel.adoc b/diaries/rust/02 - Higher-Lower-Spiel.adoc index 6a49700..42f63f5 100644 --- a/diaries/rust/02 - Higher-Lower-Spiel.adoc +++ b/diaries/rust/02 - Higher-Lower-Spiel.adoc @@ -1,5 +1,5 @@ :experimental: -:docdatetime: 2022-06-14T12:27:14.473Z +:docdatetime: 2022-08-10T17:04:53+02:00 = Erstes Spiel @@ -13,7 +13,7 @@ Das Projekt wird wie in Notebook 01 beschrieben erstellt. ~*In[2]:*~ -[source, Rust] +[source, rust] ---- :dep evcxr_input // Das ^ ist für Jupyter @@ -69,7 +69,7 @@ auftreten Für eine random Zahl brauchen wir die erste Dependency. + Also `Cargo.toml` bearbeiten: -[source,toml] +[source, toml] ---- [dependencies] rand = "0.3.14" @@ -83,7 +83,7 @@ Die crate `rand` kann jetzt im Code verwendet werden. ~*In[3]:*~ -[source, Rust] +[source, rust] ---- :dep rand = "0.3.15" // Das ^ ist von Jupyter @@ -109,7 +109,7 @@ Ein Fehler? ~*In[4]:*~ -[source, Rust] +[source.notCompiling, rust] ---- use std::cmp::Ordering; @@ -138,7 +138,7 @@ Wir müssen unser guess also umwandeln: ~*In[5]:*~ -[source, Rust] +[source, rust] ---- let guess: u32 = guess.trim().parse().expect("Please type a number!"); ---- @@ -154,7 +154,7 @@ Jetzt sollte das Vergleichen auch klappen! ~*In[6]:*~ -[source, Rust] +[source, rust] ---- use std::cmp::Ordering; @@ -178,7 +178,7 @@ Damit wir mehrmals raten können, brauchen wir eine Schleife. ~*In[7]:*~ -[source, Rust] +[source, rust] ---- let secret_number: u32 = rand::thread_rng().gen_range(1, 101); loop { @@ -226,7 +226,7 @@ Offensichtlich müssen wir die Schleife dann abbrechen. ~*In[8]:*~ -[source, Rust] +[source, rust] ---- let secret_number: u32 = rand::thread_rng().gen_range(1, 101); loop { @@ -277,7 +277,7 @@ Zahl eingibt. Das können wir auch relativ einfach fixen: ~*In[9]:*~ -[source, Rust] +[source, rust] ---- loop { let mut guess = evcxr_input::get_string("Number? "); diff --git a/diaries/rust/03 - Concepts.adoc b/diaries/rust/03 - Concepts.adoc index 4d5491f..296100d 100644 --- a/diaries/rust/03 - Concepts.adoc +++ b/diaries/rust/03 - Concepts.adoc @@ -1,5 +1,5 @@ :experimental: -:docdatetime: 2022-07-05T17:57:48+02:00 +:docdatetime: 2022-08-10T17:04:53+02:00 = Konzepte @@ -14,7 +14,7 @@ In anderen Sprachen ist das häufig `const` - in Rust gibt es aber auch `const`! Das Folgende funktioniert also nicht: -[source, Rust] +[source.notCompiling, Rust] ---- fn main() { let x = "Hello world!"; @@ -25,7 +25,7 @@ fn main() { Damit Variablen mutable sind, muss `mut` genutzt werden: -[source, Rust] +[source, rust] ---- fn main() { let mut x = "Hello world!"; @@ -46,7 +46,7 @@ Die Konvention für Konstanten ist snake case all caps. Ein Beispiel dafür ist folgendes: -[source, Rust] +[source, rust] ---- const MINUTES_IN_A_DAY: u32 = 24 * 60; ---- @@ -59,7 +59,7 @@ Anfangs habe ich es falsch verstanden: Ich dachte Shadowing wäre, dass eine Var Allerdings ist es mehr ein "Reuse" eines alten Namens. Ein Beispiel: -[source, Rust] +[source, rust] ---- fn main() { let x = 5; @@ -86,7 +86,7 @@ Wenn das nicht geht, muss manuell ein Typ festgelegt werden. Ein Beispiel: -[source, Rust] +[source, rust] ---- let guess: u32 = "42".parse().expect("Not a number!"); ---- @@ -119,7 +119,7 @@ Interessant ist, dass es zusätzliche Methoden für alles gibt (nicht nur `add`) - `overflowing_add` gibt einen Boolean, ob ein Overflow auftritt - `saturating_add` bleibt beim Maximum oder Minimum des verfügbaren Bereiches -[source, Rust] +[source, rust] ---- let number: u8 = 254; println!("{}", number.wrapping_add(2)); @@ -151,7 +151,7 @@ Sie sind aber schreibbar, wenn `mut` zur Initialisierung genutzt wird, also nich Ein paar Beispiele als Code: -[source, Rust] +[source, rust] ---- let x: (f32, char, u8) = (1.0, '🐧', 3); //_x.0 = 2.0; // geht nicht, da x nicht mut ist. @@ -178,7 +178,7 @@ Für "Arrays" mit veränderbarer Länge gibt es Vektoren. Wieder etwas Code: -[source, Rust] +[source, rust] ---- let x: [i32; 5] = [1, 2, 3, 4, 5]; // ^ so sieht der Datentyp aus @@ -198,7 +198,7 @@ Sind wie normale Funktionen in C auch. Keyword ist `fn`. Beispiel: -[source, Rust] +[source, rust] ---- fn calculate_sum(a: i32, b: i32) -> i64 { // Statements können natürlich normal genutzt werden @@ -233,7 +233,7 @@ Es gibt auch noch spezielle Docstrings, aber das kommt später. Beispiel für labels: -[source, Rust] +[source, rust] ---- fn main() { 'outer: loop { @@ -253,7 +253,7 @@ fn main() { `break` mit Wert ist Rückgabe. Einfaches Beispiel: -[source, Rust] +[source, rust] ---- fn main() { let mut counter = 0; @@ -279,7 +279,7 @@ fn main() { Looped durch eine Collection (wie in Python). -[source, Rust] +[source, rust] ---- fn main() { let a = [10, 20, 30, 40, 50]; diff --git a/diaries/rust/04 - Ownership.adoc b/diaries/rust/04 - Ownership.adoc index 785ec6b..d72392b 100644 --- a/diaries/rust/04 - Ownership.adoc +++ b/diaries/rust/04 - Ownership.adoc @@ -1,5 +1,5 @@ :experimental: -:docdatetime: 2022-07-05T15:32:40+02:00 +:docdatetime: 2022-08-10T17:04:53+02:00 = Ownership @@ -35,7 +35,7 @@ Dieser String-Typ hat den Vorteil, dass er eine dynamische Länge hat und damit Ein Beispiel: -[source, Rust] +[source, rust] ---- let mut x = String::from("Hello"); // Legt "dynamischen" String an x.push_str(" world!"); // Konkatiniert an den String @@ -48,7 +48,7 @@ Theoretisch kann `x` natürlich dann überschrieben werden, mit einem String and === Move -[source, Rust] +[source, rust] ---- let x = 5; // Int -> feste Größe und auf Stack let y = x; @@ -110,7 +110,7 @@ Damit werden (unter anderem) Race Conditions schon beim Compilen verhindert. === Dangling references -[source, Rust] +[source.notCompiling, rust] ---- fn dangle() -> &String { let s = String::from("hello"); @@ -133,7 +133,7 @@ Nur so kann multithreading etc. funktionieren. Dafür hat Rust den Slice-Datentyp. Der funktioniert ähnlich wie Array-Ranges in Python. -[source, Rust] +[source, rust] ---- let s = String::from("hello world"); diff --git a/diaries/rust/05 - Structs.adoc b/diaries/rust/05 - Structs.adoc index 6c3fdb9..2ac2397 100644 --- a/diaries/rust/05 - Structs.adoc +++ b/diaries/rust/05 - Structs.adoc @@ -1,5 +1,5 @@ :experimental: -:docdatetime: 2022-07-05T17:45:24+02:00 +:docdatetime: 2022-08-10T17:04:53+02:00 = Structs @@ -17,7 +17,7 @@ Man kann damit neue Datentypen machen. === "Normale" Structs -[source, Rust] +[source, rust] ---- struct User { active: bool, @@ -47,7 +47,7 @@ Wenn die Variable heißt wie das Feld, kann man auch statt `email: email` einfac Wenn man ein neues Struct aus einem alten mit Updates erstellen will, geht das auch mit einer Art Spread-Parameter: -[source, Rust] +[source, rust] ---- let user2 = User { email: String::from("another@example.com"), @@ -62,7 +62,7 @@ Hätten wir jetzt auch noch einen neuen `username` gesetzt (auch ein String) und === Tupel Structs -[source, Rust] +[source, rust] ---- struct RGBColor(u8, u8, u8); @@ -75,7 +75,7 @@ Sind nutzbar wie Tupel (destrucuture und `.index` zum Zugriff auf Werte), allerd === Unit-Like Structs -[source, Rust] +[source, rust] ---- struct AlwaysEqual; ---- @@ -119,7 +119,7 @@ Tatsächlich ist der sehr einfach und sehr OOPig. Die folgenden Beispiele sollten relativ viel erklären: -[source, Rust] +[source, rust] ---- struct Rectangle { width: u32, @@ -184,7 +184,7 @@ fn main() { Eine Methode kann auch `&mut self` als ersten Parameter haben. Dann können auch Felder geschrieben werden. In diesem Fall werden Referenzen aber invalidiert! -[source, Rust] +[source, rust] ---- struct Rectangle { width: u32, diff --git a/diaries/rust/06 - Enums.adoc b/diaries/rust/06 - Enums.adoc index 0c2d604..52f60b0 100644 --- a/diaries/rust/06 - Enums.adoc +++ b/diaries/rust/06 - Enums.adoc @@ -1,5 +1,5 @@ :experimental: -:docdatetime: 2022-07-25T20:00:23+02:00 +:docdatetime: 2022-08-10T17:04:53+02:00 = Enums und Pattern Matching @@ -14,7 +14,7 @@ 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] +[source, rust] ---- enum Farbcode { Hex, @@ -30,7 +30,7 @@ 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] +[source, rust] ---- enum Farbcode { Hex(String), @@ -54,7 +54,7 @@ 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] +[source, rust] ---- impl Farbcode { fn to_css_string(&self) { @@ -84,7 +84,7 @@ So wie Rust bis jetzt klang, kann wahrscheinlich jedem Datentypen ein "match-Tra Aber ganz einfach: Angenommen wir wollen die Methode `to_css_string` von oben implementieren. Diese Methode muss ja, je nach Typ, völlig unterschiedlich funktionieren. -[source, Rust] +[source, rust] ---- enum Farbcode { Hex(String), @@ -126,7 +126,7 @@ Der Unterschied ist, dass bei `other` noch der Inhalt genutzt werden kann, bei ` 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] +[source, rust] ---- #[derive(Debug)] enum Muenzwurf { diff --git a/diaries/rust/07 - Management.adoc b/diaries/rust/07 - Management.adoc index c254aa8..b6b5975 100644 --- a/diaries/rust/07 - Management.adoc +++ b/diaries/rust/07 - Management.adoc @@ -50,7 +50,7 @@ Im Hauptprogramm kann mit `mod modulname;` das Modul eingebunden werden. Gesucht Zusätzlich kann auch direkt inline ein Modul erstellt werden. Ein Beispiel: -[source.notCompiling, Rust] +[source.notCompiling, rust] ---- mod testmodul { mod nested_modul { @@ -77,7 +77,7 @@ 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] +[source, rust] ---- mod testmodul { pub mod nested_modul { @@ -114,7 +114,7 @@ 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] +[source, rust] ---- mod testmodul { pub struct Teststruct {