172 lines
4.7 KiB
Plaintext
172 lines
4.7 KiB
Plaintext
:experimental:
|
|
:docdatetime: 2022-07-05T17:31:22+02:00
|
|
|
|
= Structs
|
|
|
|
https://doc.rust-lang.org/book/ch05-00-structs.html[Link zum Buch]
|
|
|
|
== Was sind Structs
|
|
|
|
Structs kennt man ja aus C/C++.
|
|
Man kann es (denke ich) auch mit JavaScript Objekten vergleichen.
|
|
|
|
In Structs gruppiert man zusammengehöriges Zeug und hat so eine Art Pseudo-OOP.
|
|
Man kann damit neue Datentypen machen.
|
|
|
|
== How to
|
|
|
|
=== "Normale" Structs
|
|
|
|
[source, Rust]
|
|
----
|
|
struct User {
|
|
active: bool,
|
|
username: String,
|
|
email: String,
|
|
sign_in_count: u64,
|
|
}
|
|
|
|
fn main() {
|
|
let mut user1 = User {
|
|
email: String::from("someone@example.com"),
|
|
username: String::from("someusername123"),
|
|
active: true,
|
|
sign_in_count: 1,
|
|
};
|
|
|
|
println!("{}", user1.email);
|
|
|
|
user1.email = String::from("anotheremail@example.com");
|
|
}
|
|
----
|
|
|
|
Hinweis: Es können nicht einzelne Felder mutable sein, sondern wenn dann immer das ganze Struct.
|
|
|
|
==== Dinge wie in Javascript
|
|
Wenn die Variable heißt wie das Feld, kann man auch statt `email: email` einfach nur `email` schreiben.
|
|
|
|
Wenn man ein neues Struct aus einem alten mit Updates erstellen will, geht das auch mit einer Art Spread-Parameter:
|
|
|
|
[source, Rust]
|
|
----
|
|
let user2 = User {
|
|
email: String::from("another@example.com"),
|
|
..user1
|
|
};
|
|
----
|
|
|
|
`..user1` *muss* als letztes kommen und füllt dann alle bisher nicht gesetzten Felder.
|
|
Außerdem ist das etwas tricky:
|
|
Wenn die Daten, die von `user1` zu `user2` übertragen werden, gemoved werden (sprich: keine primitiven Datentypen sind), dann ist `user1` danach ungültig.
|
|
Hätten wir jetzt auch noch einen neuen `username` gesetzt (auch ein String) und nur `active` und `sign_in_count` übertragen, wäre `user1` noch gültig.
|
|
|
|
=== Tupel Structs
|
|
|
|
[source, Rust]
|
|
----
|
|
struct RGBColor(u8, u8, u8);
|
|
|
|
fn main() {
|
|
let black = Color(0, 0, 0)
|
|
}
|
|
----
|
|
|
|
Sind nutzbar wie Tupel (destrucuture und `.index` zum Zugriff auf Werte), allerdings eben ein eigener Typ.
|
|
|
|
=== Unit-Like Structs
|
|
|
|
[source, Rust]
|
|
----
|
|
struct AlwaysEqual;
|
|
----
|
|
|
|
Ein Struct muss keine Felder haben.
|
|
Das Buch meint, man könnte für diesen Datentypen jetzt noch Traits implementieren, aber davon habe ich noch keine Ahnung.
|
|
Nur dann macht diese Art von Struct irgendwie Sinn.
|
|
|
|
== Ownership der Felder
|
|
|
|
Im ersten Beispiel wird `String` satt `&str` genutzt.
|
|
Wir wollen am besten im Struct keine Referenzen, oder es müssen "named lifetime parameter" sein, etwas das wir erst später lernen.
|
|
Der Compiler wird sonst streiken.
|
|
|
|
== Das erste Mal Traits
|
|
|
|
Im Buch folgt ein Beispielprogramm für ein Struct, das ein Rechteck abbildet.
|
|
Wir wollten das ganze printen (mit `{}` als Platzhalter), allerdings implementiert Das Rechteck nicht `std::fmt::Display`.
|
|
Das scheint eine Art `toString()` für Rust zu sein.
|
|
|
|
Es gibt aber noch eine andere Möglichkeit und das haben wir schonmal für Tupel genutzt:
|
|
`{:?}` als Platzhalter (bzw. `{:#?}` für pretty print).
|
|
Dafür brauchen wir aber das Trait `Debug`.
|
|
Zum Glück scheint das aber einfach zu implementieren sein, es muss nur implementiert werden.
|
|
|
|
Der Compiler schlägt uns zwei Varianten vor:
|
|
|
|
1. `#[derive(Debug)]` über der Definition des Structs
|
|
2. `impl Debug for Rectangle` manuell
|
|
|
|
Jetzt können wir Variablen dieses Typs printen und es zeigt uns Datentyp und Felder an.
|
|
|
|
Alternativ kann man auch das Makro `dbg!(...)` nutzen.
|
|
Das wird dann auf `stderr` geprintet.
|
|
Man kann sogar ein Statement da rein packen (also zum Beispiel `30 * x`) und bekommt das Statement mit dem Ergebnis geprintet, wobei das Ergebnis (als Wert, nicht Referenz) auch zurückgegeben wird.
|
|
|
|
== Funktionen in Structs
|
|
|
|
Unser Struct soll jetzt auch eine Funktion auf sich selbst aufrufen können.
|
|
Tatsächlich ist der sehr einfach und sehr OOPig.
|
|
|
|
Die folgenden Beispiele sollten relativ viel erklären.
|
|
|
|
[source, Rust]
|
|
----
|
|
struct Rectangle {
|
|
width: u32,
|
|
height: u32,
|
|
}
|
|
|
|
impl Rectangle {
|
|
// var.area();
|
|
fn area(&self) -> u32 {
|
|
self.width * self.height
|
|
}
|
|
|
|
// Rectangle::square(5);
|
|
fn square(size: u32) -> Rectangle {
|
|
Rectangle {
|
|
width: size,
|
|
height: size,
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mehrere impl Blöcke sind erlaubt
|
|
impl Rectangle {
|
|
// var.has_same_area(&other);
|
|
fn has_same_area(&self, other: &Rectangle) -> bool {
|
|
self.area() == other.area()
|
|
}
|
|
|
|
// Rectangle::same_area(&first, &second);
|
|
fn same_area(first: &Rectangle, second: &Rectangle) -> bool {
|
|
first.area() == second.area()
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
let rect1 = Rectangle {
|
|
width: 12,
|
|
height: 3,
|
|
};
|
|
let rect2 = Rectangle::square(6);
|
|
|
|
println!("{}", rect1.area()); // 36
|
|
println!("{}", rect2.area()); // 36
|
|
|
|
println!("{}", rect1.has_same_area(&rect2)); // true
|
|
println!("{}", rect2.has_same_area(&rect1)); // true
|
|
println!("{}", Rectangle::same_area(&rect1, &rect2)); // true
|
|
}
|
|
----
|