Implemented the homework exercises
parent
75a0e0e42e
commit
51f91b8c2c
|
|
@ -2,8 +2,265 @@
|
|||
|
||||
## 1. Ownership & Borrowing:
|
||||
|
||||
## 2. Pattern Matching
|
||||
### Aufgabe 1:
|
||||
|
||||
Erkläre, warum folgender Rust-Code nicht kompiliert:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let s1 = String::from("Hallo");
|
||||
let s2 = s1;
|
||||
|
||||
println!("{}", s1);
|
||||
}
|
||||
```
|
||||
Ändere den Code so, dass ```s1``` und ```s2``` augegeben werden können.
|
||||
<br> <br>
|
||||
|
||||
|
||||
### Aufgabe 2:
|
||||
|
||||
Warum verbietet Rust folgenden Code?
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let mut text = String::from("Rust");
|
||||
|
||||
let r1 = &text;
|
||||
let r2 = &mut text;
|
||||
|
||||
println!("{}", r1);
|
||||
println!("{}", r2);
|
||||
}
|
||||
```
|
||||
Schreibe den Code so um, sodass beide ```println!``` statements funktionieren.
|
||||
|
||||
## 2. Pattern Matching & Enums:
|
||||
|
||||
### Aufgabe 3:
|
||||
Gegeben sei folgender Rust Code für eine 3-Phasen Ampel:
|
||||
```rust
|
||||
fn main(){
|
||||
enum Ampel {
|
||||
Rot,
|
||||
Gelb,
|
||||
Gruen,
|
||||
}
|
||||
|
||||
let ampel_status = Ampel::Rot;
|
||||
|
||||
match ampel_status {
|
||||
Ampel::Rot => println!("STOP"),
|
||||
Ampel::Gelb => println!("Achtung"),
|
||||
Ampel::Gruen => println!("fahren"),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Ändere den Code in ```match ampel_status``` so ab, dass auch in ```Ampel``` beliebig viele andere Farben als Fahrsignal genutzt werden können.
|
||||
|
||||
Was passiert wenn nicht alle Varainten in ```match ampel_status``` aufgelistet werden?
|
||||
|
||||
## 3. Option & Result Types:
|
||||
|
||||
### Aufgabe 4:
|
||||
|
||||
Erkläre, warum folgender Code nicht funktioniert und behebe das Problem:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let numbers = vec![1, 2, 3, 4, 5];
|
||||
let index = 10;
|
||||
|
||||
let value = numbers[index];
|
||||
println!("Wert: {}", value);
|
||||
}
|
||||
```
|
||||
|
||||
Verwende `Option` oder eine andere Fehlerbehandlung, um sicher auf den Vektor zuzugreifen. Erkläre die Vorteile dieses Ansatzes gegenüber einem Laufzeitfehler ("panic").
|
||||
|
||||
<br>
|
||||
|
||||
### Aufgabe 5:
|
||||
|
||||
Schreibe eine Funktion `parse_number`, die einen String nimmt und ein `Result<i32, String>` zurückgibt:
|
||||
- Bei erfolgreicher Konvertierung: `Ok(zahl)`
|
||||
- Bei Fehler: `Err("Ungültige Eingabe".to_string())`
|
||||
|
||||
Verwende danach die Funktion in `main()` und behandle das `Result` mit `match`.
|
||||
|
||||
```rust
|
||||
fn parse_number(input: &str) -> Result<i32, String> {
|
||||
// TODO: Implementierung
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// TODO: Funktion testen und Error Handling
|
||||
}
|
||||
```
|
||||
|
||||
Wie unterscheidet sich `Result` von `Option`? Wann verwendet man welchen Typ?
|
||||
|
||||
## 4. String vs &str und Ownership:
|
||||
|
||||
### Aufgabe 6:
|
||||
|
||||
Erkläre den Unterschied zwischen `String` und `&str`. Warum verbietet Rust folgende Funktion zu kompilieren?
|
||||
|
||||
```rust
|
||||
fn greet(name: &str) {
|
||||
println!("Hallo, {}!", name);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let my_string = String::from("Alice");
|
||||
let string_slice = "Bob";
|
||||
|
||||
greet(my_string); // Fehler?
|
||||
greet(string_slice);
|
||||
}
|
||||
```
|
||||
|
||||
Behebe den Fehler und erkläre, warum die Verwendung von `&str` als Parameter flexibler ist.
|
||||
|
||||
<br>
|
||||
|
||||
### Aufgabe 7:
|
||||
|
||||
Was ist der Unterschied zwischen folgenden zwei Funktionen? Welche ist besser und warum?
|
||||
|
||||
```rust
|
||||
// Variante 1:
|
||||
fn create_greeting_1(name: String) -> String {
|
||||
format!("Hallo, {}!", name)
|
||||
}
|
||||
|
||||
// Variante 2:
|
||||
fn create_greeting_2(name: &str) -> String {
|
||||
format!("Hallo, {}!", name)
|
||||
}
|
||||
```
|
||||
|
||||
Schreibe für beide Varianten ein Beispiel in `main()`, das die Flexibilität von Variante 2 demonstriert.
|
||||
|
||||
## 5. Traits und Polymorphismus:
|
||||
|
||||
### Aufgabe 8:
|
||||
|
||||
Gegeben sind folgende Structs und ein Trait:
|
||||
|
||||
```rust
|
||||
struct Kreis {
|
||||
radius: f64,
|
||||
}
|
||||
|
||||
struct Rechteck {
|
||||
width: f64,
|
||||
height: f64,
|
||||
}
|
||||
|
||||
trait Flaeche {
|
||||
fn flaeche(&self) -> f64;
|
||||
}
|
||||
```
|
||||
|
||||
Implementiere den `Flaeche` Trait für beide Structs. Schreibe dann eine Funktion, die eine Referenz auf ein Objekt mit `Flaeche` Trait annimmt und dessen Fläche ausgibt.
|
||||
|
||||
Bonus: Erstelle einen Vektor mit verschiedenen Formen und iteriere über sie.
|
||||
|
||||
|
||||
## 6. Mutability und Interior Mutability:
|
||||
|
||||
### Aufgabe 9:
|
||||
|
||||
Erkläre, warum folgender Code nicht funktioniert:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let counter = 0;
|
||||
counter += 1;
|
||||
println!("Counter: {}", counter);
|
||||
}
|
||||
```
|
||||
|
||||
Wie würde man diesen Code korrekt schreiben? Was ist der Unterschied zwischen `let mut` und Immutability?
|
||||
|
||||
Gegeben ist ein Programm, das einen Counter mit einer Funktion erhöht:
|
||||
|
||||
```rust
|
||||
fn increment(counter: &mut i32) {
|
||||
*counter += 1;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut count = 0;
|
||||
increment(&mut count);
|
||||
println!("Counter: {}", count);
|
||||
}
|
||||
```
|
||||
|
||||
Erkläre die Rolle von `mut`, `&mut` und `*`.
|
||||
|
||||
## 7. Lifetimes:
|
||||
|
||||
### Aufgabe 10:
|
||||
|
||||
Warum kompiliert folgender Code nicht und wie behebt man das Problem?
|
||||
|
||||
```rust
|
||||
fn longest(s1: &str, s2: &str) -> &str {
|
||||
if s1.len() > s2.len() {
|
||||
s1
|
||||
} else {
|
||||
s2
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let string1 = String::from("abc");
|
||||
let result;
|
||||
{
|
||||
let string2 = String::from("xyz");
|
||||
result = longest(&string1, &string2);
|
||||
}
|
||||
println!("{}", result);
|
||||
}
|
||||
```
|
||||
|
||||
Erkläre, was Lifetimes sind und warum Rust sie braucht. Behebe den Code mit korrekten Lifetime-Annotationen.
|
||||
|
||||
|
||||
## 9. Error Handling und Panic:
|
||||
|
||||
### Aufgabe 11:
|
||||
|
||||
Was ist der Unterschied zwischen `panic!` und `Result`? Wann sollte man welches verwenden?
|
||||
|
||||
Schreibe eine Funktion `divide`, die zwei Zahlen dividiert:
|
||||
- Bei erfolgreicher Division: Rückgabe des Ergebnisses
|
||||
- Bei Division durch Null: Rückgabe eines fehlers als `Result<f64, String>`
|
||||
|
||||
```rust
|
||||
fn divide(a: f64, b: f64) -> Result<f64, String> {
|
||||
// TODO
|
||||
}
|
||||
|
||||
fn main() {
|
||||
match divide(10.0, 2.0) {
|
||||
Ok(result) => println!("Ergebnis: {}", result),
|
||||
Err(e) => println!("Fehler: {}", e),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Erkläre, warum `Result` sicherer ist als ein `panic!` in einer Library-Funktion.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 3. Enums
|
||||
|
||||
## 4. Lifetimes
|
||||
Loading…
Reference in New Issue