1076 lines
24 KiB
Markdown
1076 lines
24 KiB
Markdown
# Rust — Základy na dril
|
|
|
|
Každé cvičenie: prečítaj, napíš kód, skompiluj, porovnaj s riešením na konci.
|
|
Riešenia sú číslované na konci súboru. **Nepozeraj dopredu.**
|
|
|
|
---
|
|
|
|
## Časť A: Result a Option — kedy čo
|
|
|
|
`Result<T, E>` = operácia, ktorá môže zlyhať (súbor, parsing, sieť)
|
|
`Option<T>` = hodnota, ktorá môže chýbať (hľadanie, konverzia)
|
|
|
|
### A1. Konverzie medzi Result a Option
|
|
|
|
Napíš, čo vráti každý výraz. Nekompiluj, najprv tipni, potom over.
|
|
|
|
```rust
|
|
fn main() {
|
|
let a: Result<i32, &str> = Ok(5);
|
|
let b: Result<i32, &str> = Err("chyba");
|
|
|
|
println!("{:?}", a.ok()); // ?
|
|
println!("{:?}", b.ok()); // ?
|
|
println!("{:?}", a.is_ok()); // ?
|
|
println!("{:?}", b.is_ok()); // ?
|
|
println!("{:?}", a.is_err()); // ?
|
|
println!("{:?}", b.is_err()); // ?
|
|
|
|
let c: Option<i32> = Some(10);
|
|
let d: Option<i32> = None;
|
|
|
|
println!("{:?}", c.ok_or("prazdne")); // ?
|
|
println!("{:?}", d.ok_or("prazdne")); // ?
|
|
println!("{:?}", c.is_some()); // ?
|
|
println!("{:?}", d.is_none()); // ?
|
|
}
|
|
```
|
|
|
|
### A2. .ok() vs .is_ok() — kedy ktoré
|
|
|
|
Máš funkciu:
|
|
```rust
|
|
fn nacitaj(cesta: &str) -> Result<String, std::io::Error> {
|
|
std::fs::read_to_string(cesta)
|
|
}
|
|
```
|
|
|
|
Napíš 3 rôzne funkcie, každá spracuje výsledok inak:
|
|
|
|
1. `pouzij_ok()` — vráti `Option<String>` (zahoď chybu, zaujíma ťa len hodnota)
|
|
2. `pouzij_is_ok()` — vráti `bool` (zaujíma ťa len či sa podarilo)
|
|
3. `pouzij_match()` — vypíše obsah ak Ok, vypíše chybu ak Err
|
|
|
|
### A3. Reťazenie .ok()
|
|
|
|
Napíš funkciu `parsuj_cislo(text: &str) -> Option<i32>` ktorá:
|
|
1. Vezme `text`
|
|
2. Skúsi ho sparsovať na `i32` (`text.parse::<i32>()` vracia `Result`)
|
|
3. Vráti `Option<i32>`
|
|
|
|
Použi **jeden riadok** bez `match`, bez `if`.
|
|
|
|
### A4. .unwrap_or() a .unwrap_or_default()
|
|
|
|
Napíš čo vypíše:
|
|
```rust
|
|
fn main() {
|
|
let a: Option<i32> = Some(5);
|
|
let b: Option<i32> = None;
|
|
let c: Result<i32, &str> = Err("zle");
|
|
|
|
println!("{}", a.unwrap_or(0));
|
|
println!("{}", b.unwrap_or(0));
|
|
println!("{}", b.unwrap_or_default());
|
|
println!("{}", c.unwrap_or(99));
|
|
println!("{}", c.unwrap_or_default());
|
|
|
|
let d: Option<String> = None;
|
|
println!("{:?}", d.unwrap_or_default());
|
|
println!("{:?}", d.unwrap_or("fallback".to_string()));
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Časť B: Operátor ? (question mark)
|
|
|
|
### B1. Základné použitie
|
|
|
|
Táto funkcia je napísaná s match. Prepíš ju tak, aby používala `?` a bola čo najkratšia:
|
|
|
|
```rust
|
|
fn nacitaj_a_parsuj(cesta: &str) -> Result<i32, Box<dyn std::error::Error>> {
|
|
let obsah = match std::fs::read_to_string(cesta) {
|
|
Ok(s) => s,
|
|
Err(e) => return Err(Box::new(e)),
|
|
};
|
|
let cislo = match obsah.trim().parse::<i32>() {
|
|
Ok(n) => n,
|
|
Err(e) => return Err(Box::new(e)),
|
|
};
|
|
Ok(cislo)
|
|
}
|
|
```
|
|
|
|
### B2. ? v Option kontexte
|
|
|
|
Prepíš s operátorom `?` (funkcia vracia `Option`, nie `Result`):
|
|
|
|
```rust
|
|
fn prvy_sused(vektor: &[i32], index: usize) -> Option<i32> {
|
|
if index == 0 {
|
|
return None;
|
|
}
|
|
let predchadzajuci = index.checked_sub(1);
|
|
match predchadzajuci {
|
|
Some(i) => {
|
|
if i < vektor.len() {
|
|
Some(vektor[i])
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
None => None,
|
|
}
|
|
}
|
|
```
|
|
|
|
### B3. .ok()? vzor
|
|
|
|
Napíš funkciu `nacitaj_json(cesta: &str) -> Option<Vec<String>>` ktorá:
|
|
1. Prečíta súbor (Result) → ak zlyhá, vráti None
|
|
2. Deserializuje JSON na `Vec<String>` (Result) → ak zlyhá, vráti None
|
|
3. Vráti `Some(vec)`
|
|
|
|
Použi `.ok()?` vzor. Celá funkcia na 3 riadky (vrátane signatúry).
|
|
|
|
### B4. Kde ? NEMÔŽEŠ použiť
|
|
|
|
Čo je zle na tomto kóde? Oprav ho (2 rôzne spôsoby):
|
|
|
|
```rust
|
|
fn main() {
|
|
let obsah = std::fs::read_to_string("subor.txt")?;
|
|
println!("{}", obsah);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Časť C: let...else vs if let vs match
|
|
|
|
### C1. Kedy ktoré — prepíš
|
|
|
|
Máš 3 verzie toho istého. Pre každú situáciu nižšie vyber najlepší zápis a napíš ho:
|
|
|
|
**Situácia 1:** Chceš hodnotu z Option, ak None tak return None z funkcie.
|
|
```rust
|
|
// Verzia A: match
|
|
let hodnota = match mozno_hodnota {
|
|
Some(v) => v,
|
|
None => return None,
|
|
};
|
|
|
|
// Verzia B: if let (??? je toto správne?)
|
|
|
|
// Verzia C: let...else
|
|
```
|
|
Napíš verziu B a C. Ktorá je najkratšia?
|
|
|
|
**Situácia 2:** Chceš niečo urobiť len ak je Some, inak nič.
|
|
```rust
|
|
// Ktoré je lepšie — if let alebo match?
|
|
```
|
|
Napíš obe verzie.
|
|
|
|
**Situácia 3:** Chceš rôzne akcie pre Ok a Err.
|
|
```rust
|
|
// Ktoré je lepšie — match, if let, alebo let...else?
|
|
```
|
|
Napíš všetky 3, rozhodni sa.
|
|
|
|
### C2. let...else v praxi
|
|
|
|
Napíš funkciu `spracuj(text: &str) -> Option<String>` ktorá:
|
|
1. Skúsi sparsovať `text` na `u32`. Ak sa nepodarí → vráť None.
|
|
2. Ak číslo > 100, vráť None.
|
|
3. Vráť `Some(format!("Číslo: {}", cislo))`
|
|
|
|
Napíš to dvakrát:
|
|
- Raz s `let...else`
|
|
- Raz bez `let...else` (s `?` alebo `match`)
|
|
|
|
### C3. if let vs let...else
|
|
|
|
Vysvetli sám sebe (komentárom v kóde): kedy `if let` a kedy `let...else`?
|
|
|
|
Napíš funkciu `najdi_a_vypis(zoznam: &[String], hladany: &str)` ktorá:
|
|
- Nájde prvý reťazec obsahujúci `hladany`
|
|
- Ak nájde → vypíše ho
|
|
- Ak nenájde → vypíše "Nenájdené"
|
|
|
|
Napíš to s `if let`. Potom to prepíš s `let...else`. Ktoré je prirodzenejšie tu?
|
|
|
|
---
|
|
|
|
## Časť D: Iterátory vs cykly
|
|
|
|
### D1. for cyklus → iterátor
|
|
|
|
Prepíš na iterátorový zápis (bez `for`, bez `mut`):
|
|
|
|
```rust
|
|
fn pocet_kladnych(cisla: &[i32]) -> usize {
|
|
let mut pocet = 0;
|
|
for c in cisla {
|
|
if *c > 0 {
|
|
pocet += 1;
|
|
}
|
|
}
|
|
pocet
|
|
}
|
|
```
|
|
|
|
### D2. filter + collect
|
|
|
|
Napíš funkciu `dlhe_slova(slova: &[String], min_dlzka: usize) -> Vec<&String>` ktorá vráti referencie na slová dlhšie ako `min_dlzka`. Jeden riadok.
|
|
|
|
### D3. find
|
|
|
|
Napíš funkciu `najdi_podla_zaciatku(slova: &[String], prefix: &str) -> Option<&String>` ktorá vráti prvé slovo začínajúce daným prefixom. Jeden riadok.
|
|
|
|
### D4. map + collect
|
|
|
|
Prepíš bez `for`:
|
|
```rust
|
|
fn zdvojnasob(cisla: &[i32]) -> Vec<i32> {
|
|
let mut vysledok = Vec::new();
|
|
for c in cisla {
|
|
vysledok.push(c * 2);
|
|
}
|
|
vysledok
|
|
}
|
|
```
|
|
|
|
### D5. position (na remove z Vec)
|
|
|
|
Napíš funkciu `odstran_podla_mena(zoznam: &mut Vec<String>, meno: &str) -> Result<String, ()>`:
|
|
- Nájdi index prvku s daným menom
|
|
- Odstráň ho z vektora a vráť ho
|
|
- Ak neexistuje → Err(())
|
|
|
|
Použi `.position()` a `.remove()`.
|
|
|
|
### D6. any / all
|
|
|
|
Bez cyklu napíš:
|
|
1. `obsahuje_zaporne(cisla: &[i32]) -> bool` — true ak aspoň jedno < 0
|
|
2. `vsetky_neprazdne(texty: &[String]) -> bool` — true ak žiadny nie je prázdny
|
|
|
|
### D7. fold / sum
|
|
|
|
Napíš `priemer(cisla: &[f64]) -> Option<f64>`:
|
|
- Ak je prázdny slice → None
|
|
- Inak vráť priemer
|
|
|
|
Použi `.iter().sum::<f64>()` alebo `.fold()`.
|
|
|
|
### D8. enumerate
|
|
|
|
Napíš funkciu `vypis_s_cislami(polozky: &[String])` ktorá vypíše:
|
|
```
|
|
1. prvá položka
|
|
2. druhá položka
|
|
```
|
|
Použi `.enumerate()`.
|
|
|
|
### D9. iter() vs into_iter() vs iter_mut()
|
|
|
|
Čo je zle na tomto kóde? Oprav:
|
|
```rust
|
|
fn zvys_vsetky(cisla: &mut Vec<i32>) {
|
|
for c in cisla.iter() {
|
|
*c += 1;
|
|
}
|
|
}
|
|
```
|
|
|
|
### D10. Reťazenie iterátorov
|
|
|
|
Napíš jedným výrazom (reťazením):
|
|
Máš `Vec<String>` mien. Chceš nájsť počet mien, ktoré začínajú na 'A' a sú dlhšie ako 3 znaky.
|
|
|
|
```rust
|
|
fn pocet_dlhych_a_mien(mena: &[String]) -> usize {
|
|
// jeden riadok
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Časť E: Closures (uzávery / anonymné funkcie)
|
|
|
|
### E1. Syntax
|
|
|
|
Prepíš `map` s closure tromi rôznymi zápismi:
|
|
```rust
|
|
let cisla = vec![1, 2, 3];
|
|
// 1. plný zápis s typmi
|
|
// 2. skrátený zápis
|
|
// 3. najkratší možný zápis
|
|
```
|
|
|
|
### E2. Closure vs funkcia
|
|
|
|
Napíš filter dvoma spôsobmi:
|
|
```rust
|
|
let slova = vec!["ahoj".to_string(), "svet".to_string(), "a".to_string()];
|
|
// 1. s closure
|
|
let dlhe: Vec<&String> = slova.iter().filter(/* closure */).collect();
|
|
// 2. s pomenovanou funkciou
|
|
fn je_dlhe(s: &&String) -> bool { /* */ }
|
|
let dlhe: Vec<&String> = slova.iter().filter(je_dlhe).collect();
|
|
```
|
|
|
|
### E3. move v closure
|
|
|
|
Čo je zle? Oprav:
|
|
```rust
|
|
fn vytvor_pozdrav(meno: String) -> impl Fn() -> String {
|
|
let closure = || format!("Ahoj, {}!", meno);
|
|
closure
|
|
}
|
|
```
|
|
|
|
### E4. Closure zachytávajúci &mut
|
|
|
|
Napíš kód, ktorý:
|
|
1. Má `Vec<i32>` s hodnotami [1, 2, 3]
|
|
2. Vytvorí closure, ktorý pridá číslo do vektora
|
|
3. Zavolaj closure 3x s hodnotami 4, 5, 6
|
|
4. Vypíš výsledný vektor
|
|
|
|
---
|
|
|
|
## Časť F: Kombinované cvičenia (ako na skúške)
|
|
|
|
### F1. Kompletný CRUD s Option/Result
|
|
|
|
Implementuj (bez pozerania na riešenia predtým):
|
|
|
|
```rust
|
|
struct Zamestnanec {
|
|
meno: String,
|
|
plat: u32,
|
|
}
|
|
|
|
struct Firma {
|
|
zamestnanci: Vec<Zamestnanec>,
|
|
}
|
|
|
|
impl Firma {
|
|
fn new() -> Firma { /* */ }
|
|
|
|
// Pridaj zamestnanca. Ak meno existuje → Err(())
|
|
fn pridaj(&mut self, z: Zamestnanec) -> Result<(), ()> { /* */ }
|
|
|
|
// Odstráň podľa mena. Vráť odstranenéno. Ak neexistuje → Err(())
|
|
fn odstran(&mut self, meno: &str) -> Result<Zamestnanec, ()> { /* */ }
|
|
|
|
// Nájdi podľa mena
|
|
fn najdi(&self, meno: &str) -> Option<&Zamestnanec> { /* */ }
|
|
|
|
// Všetci s platom nad X
|
|
fn s_platom_nad(&self, min: u32) -> Vec<&Zamestnanec> { /* */ }
|
|
|
|
// Priemerný plat. None ak nikto.
|
|
fn priemerny_plat(&self) -> Option<f64> { /* */ }
|
|
|
|
// Najdi najlepsie plateneho
|
|
fn najlepsi_plat(&self) -> Option<&Zamestnanec> { /* */ }
|
|
}
|
|
```
|
|
|
|
### F2. File I/O kombinácia
|
|
|
|
Napíš obe funkcie pre `Firma` (s derive Serialize, Deserialize):
|
|
```rust
|
|
fn nacitaj_zo_suboru(cesta: &std::path::PathBuf) -> Option<Firma>
|
|
fn uloz_do_suboru(&self, cesta: &std::path::PathBuf) -> bool
|
|
```
|
|
|
|
### F3. CLI parsovanie vstupu
|
|
|
|
Napíš funkciu v `main()` (bez clap), ktorá:
|
|
1. Prečíta argumenty z príkazového riadku (`std::env::args()`)
|
|
2. Prvý argument je príkaz: "pridaj", "odstran", "zoznam"
|
|
3. Pre "pridaj": ďalšie 2 argumenty sú meno a plat
|
|
4. Pre "odstran": ďalší argument je meno
|
|
5. Pre "zoznam": žiadne ďalšie argumenty
|
|
|
|
Použi `let args: Vec<String> = std::env::args().collect();` a potom match/if na `args[1]`.
|
|
|
|
### F4. Všetko dokopy
|
|
|
|
Napíš kompletný program (lib.rs + main.rs) pre jednoduchý **Slovník** (prekladový):
|
|
|
|
```
|
|
Štruktúra Slovo: original (String), preklad (String), kategoria (String)
|
|
Štruktúra Slovnik: slova: Vec<Slovo>
|
|
+ Serialize, Deserialize, Default
|
|
|
|
Metódy:
|
|
- nacitaj_zo_suboru(cesta) -> Option<Slovnik>
|
|
- uloz_do_suboru(cesta) -> bool
|
|
- pridaj_slovo(slovo) -> Result<(), ()> (duplicita podľa original)
|
|
- odstran_slovo(original: &str) -> Result<Slovo, ()>
|
|
- preloz(original: &str) -> Option<&str> (vráti preklad)
|
|
- slova_v_kategorii(kat: &str) -> Vec<&Slovo>
|
|
- pocet_v_kategoriach() -> HashMap<String, usize>
|
|
```
|
|
|
|
---
|
|
|
|
## Časť G: Špeciálne vzory
|
|
|
|
### G1. or_insert / entry API
|
|
|
|
Napíš funkciu `pocitaj_znaky(text: &str) -> HashMap<char, usize>`:
|
|
Počíta výskyt každého znaku v texte. Použi `.entry().or_insert(0)`.
|
|
|
|
### G2. Option::map
|
|
|
|
Prepíš bez match/if:
|
|
```rust
|
|
fn zdvojnasob_ak_existuje(x: Option<i32>) -> Option<i32> {
|
|
match x {
|
|
Some(n) => Some(n * 2),
|
|
None => None,
|
|
}
|
|
}
|
|
```
|
|
|
|
### G3. Option::and_then (flatmap)
|
|
|
|
Napíš funkciu:
|
|
```rust
|
|
// Máš Option<String>. Ak Some, skús parsovať na i32. Ak sa podarí, vráť Some(i32). Inak None.
|
|
fn parsuj_option(maybe_text: Option<String>) -> Option<i32>
|
|
```
|
|
Použi `.and_then()`. Jeden riadok.
|
|
|
|
### G4. Result::map_err
|
|
|
|
Napíš funkciu:
|
|
```rust
|
|
// Konvertuj io::Error na String
|
|
fn nacitaj(cesta: &str) -> Result<String, String> {
|
|
// std::fs::read_to_string vracia Result<String, io::Error>
|
|
// ty chceš Result<String, String>
|
|
}
|
|
```
|
|
Použi `.map_err()`.
|
|
|
|
### G5. .flatten()
|
|
|
|
Čo vráti?
|
|
```rust
|
|
let a: Option<Option<i32>> = Some(Some(5));
|
|
let b: Option<Option<i32>> = Some(None);
|
|
let c: Option<Option<i32>> = None;
|
|
|
|
println!("{:?}", a.flatten());
|
|
println!("{:?}", b.flatten());
|
|
println!("{:?}", c.flatten());
|
|
```
|
|
|
|
---
|
|
|
|
---
|
|
|
|
---
|
|
|
|
# RIEŠENIA
|
|
|
|
## A1
|
|
|
|
```
|
|
Some(5)
|
|
None
|
|
true
|
|
false
|
|
false
|
|
true
|
|
Ok(10)
|
|
Err("prazdne")
|
|
true
|
|
true
|
|
```
|
|
|
|
## A2
|
|
|
|
```rust
|
|
fn pouzij_ok(cesta: &str) -> Option<String> {
|
|
nacitaj(cesta).ok()
|
|
}
|
|
|
|
fn pouzij_is_ok(cesta: &str) -> bool {
|
|
nacitaj(cesta).is_ok()
|
|
}
|
|
|
|
fn pouzij_match(cesta: &str) {
|
|
match nacitaj(cesta) {
|
|
Ok(obsah) => println!("{}", obsah),
|
|
Err(e) => println!("Chyba: {}", e),
|
|
}
|
|
}
|
|
```
|
|
|
|
## A3
|
|
|
|
```rust
|
|
fn parsuj_cislo(text: &str) -> Option<i32> {
|
|
text.parse::<i32>().ok()
|
|
}
|
|
```
|
|
|
|
## A4
|
|
|
|
```
|
|
5
|
|
0
|
|
0
|
|
99
|
|
0
|
|
""
|
|
"fallback"
|
|
```
|
|
|
|
Pozn.: `unwrap_or_default()` — pre `i32` default je `0`, pre `String` je `""`.
|
|
|
|
## B1
|
|
|
|
```rust
|
|
fn nacitaj_a_parsuj(cesta: &str) -> Result<i32, Box<dyn std::error::Error>> {
|
|
let obsah = std::fs::read_to_string(cesta)?;
|
|
let cislo = obsah.trim().parse::<i32>()?;
|
|
Ok(cislo)
|
|
}
|
|
```
|
|
|
|
## B2
|
|
|
|
```rust
|
|
fn prvy_sused(vektor: &[i32], index: usize) -> Option<i32> {
|
|
let i = index.checked_sub(1)?;
|
|
Some(*vektor.get(i)?)
|
|
}
|
|
```
|
|
|
|
## B3
|
|
|
|
```rust
|
|
fn nacitaj_json(cesta: &str) -> Option<Vec<String>> {
|
|
let raw = std::fs::read_to_string(cesta).ok()?;
|
|
serde_json::from_str(&raw).ok()
|
|
}
|
|
```
|
|
|
|
## B4
|
|
|
|
`main()` vracia `()`, nie `Result`. Operátor `?` sa nedá použiť vo funkcii, ktorá nevracia `Result` alebo `Option`.
|
|
|
|
Oprava 1 — main vracia Result:
|
|
```rust
|
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
let obsah = std::fs::read_to_string("subor.txt")?;
|
|
println!("{}", obsah);
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
Oprava 2 — match/unwrap:
|
|
```rust
|
|
fn main() {
|
|
match std::fs::read_to_string("subor.txt") {
|
|
Ok(obsah) => println!("{}", obsah),
|
|
Err(e) => eprintln!("Chyba: {}", e),
|
|
}
|
|
}
|
|
```
|
|
|
|
## C1
|
|
|
|
**Situácia 1:**
|
|
```rust
|
|
// Verzia B: if let — NEDÁ SA dobre, lebo chceš pokračovať s hodnotou
|
|
// if let Some(v) = mozno_hodnota { v } else { return None }
|
|
// Toto nie je pekné, lebo v žije len v if bloku.
|
|
|
|
// Verzia C: let...else
|
|
let Some(hodnota) = mozno_hodnota else {
|
|
return None;
|
|
};
|
|
|
|
// Najkratšia: operátor ?
|
|
let hodnota = mozno_hodnota?;
|
|
```
|
|
|
|
**Situácia 2:**
|
|
```rust
|
|
// if let je lepšie — nechceš else vetvu
|
|
if let Some(v) = mozno_hodnota {
|
|
println!("Mám: {}", v);
|
|
}
|
|
|
|
// match je zbytočne dlhý:
|
|
match mozno_hodnota {
|
|
Some(v) => println!("Mám: {}", v),
|
|
None => {} // zbytočný riadok
|
|
}
|
|
```
|
|
|
|
**Situácia 3:**
|
|
```rust
|
|
// match je najlepší — máš 2 rôzne vetvy
|
|
match vysledok {
|
|
Ok(v) => println!("OK: {}", v),
|
|
Err(e) => println!("Chyba: {}", e),
|
|
}
|
|
|
|
// if let — len ak ťa zaujíma jedna vetva:
|
|
if let Ok(v) = vysledok {
|
|
println!("OK: {}", v);
|
|
}
|
|
|
|
// let...else — ak chceš hodnotu a pri chybe return:
|
|
let Ok(v) = vysledok else {
|
|
println!("Chyba!");
|
|
return;
|
|
};
|
|
println!("OK: {}", v);
|
|
```
|
|
|
|
## C2
|
|
|
|
```rust
|
|
// S let...else:
|
|
fn spracuj(text: &str) -> Option<String> {
|
|
let Ok(cislo) = text.parse::<u32>() else {
|
|
return None;
|
|
};
|
|
if cislo > 100 {
|
|
return None;
|
|
}
|
|
Some(format!("Číslo: {}", cislo))
|
|
}
|
|
|
|
// S ? a filter:
|
|
fn spracuj(text: &str) -> Option<String> {
|
|
let cislo = text.parse::<u32>().ok()?;
|
|
if cislo > 100 {
|
|
return None;
|
|
}
|
|
Some(format!("Číslo: {}", cislo))
|
|
}
|
|
```
|
|
|
|
## C3
|
|
|
|
```rust
|
|
// if let — keď chceš NIEČO UROBIŤ s hodnotou, ale nepotrebuješ ju ďalej
|
|
// let...else — keď POTREBUJEŠ HODNOTU ďalej v kóde a chceš early return
|
|
|
|
fn najdi_a_vypis(zoznam: &[String], hladany: &str) {
|
|
// if let je tu prirodzenejšie — netreba hodnotu ďalej:
|
|
if let Some(najdeny) = zoznam.iter().find(|s| s.contains(hladany)) {
|
|
println!("{}", najdeny);
|
|
} else {
|
|
println!("Nenájdené");
|
|
}
|
|
|
|
// let...else je tu menej prirodzené:
|
|
let Some(najdeny) = zoznam.iter().find(|s| s.contains(hladany)) else {
|
|
println!("Nenájdené");
|
|
return;
|
|
};
|
|
println!("{}", najdeny);
|
|
}
|
|
```
|
|
|
|
## D1
|
|
|
|
```rust
|
|
fn pocet_kladnych(cisla: &[i32]) -> usize {
|
|
cisla.iter().filter(|c| **c > 0).count()
|
|
}
|
|
```
|
|
|
|
## D2
|
|
|
|
```rust
|
|
fn dlhe_slova(slova: &[String], min_dlzka: usize) -> Vec<&String> {
|
|
slova.iter().filter(|s| s.len() > min_dlzka).collect()
|
|
}
|
|
```
|
|
|
|
## D3
|
|
|
|
```rust
|
|
fn najdi_podla_zaciatku<'a>(slova: &'a [String], prefix: &str) -> Option<&'a String> {
|
|
slova.iter().find(|s| s.starts_with(prefix))
|
|
}
|
|
```
|
|
|
|
## D4
|
|
|
|
```rust
|
|
fn zdvojnasob(cisla: &[i32]) -> Vec<i32> {
|
|
cisla.iter().map(|c| c * 2).collect()
|
|
}
|
|
```
|
|
|
|
## D5
|
|
|
|
```rust
|
|
fn odstran_podla_mena(zoznam: &mut Vec<String>, meno: &str) -> Result<String, ()> {
|
|
let pos = zoznam.iter().position(|s| s == meno).ok_or(())?;
|
|
Ok(zoznam.remove(pos))
|
|
}
|
|
```
|
|
|
|
Alternatíva bez `?`:
|
|
```rust
|
|
fn odstran_podla_mena(zoznam: &mut Vec<String>, meno: &str) -> Result<String, ()> {
|
|
if let Some(pos) = zoznam.iter().position(|s| s == meno) {
|
|
Ok(zoznam.remove(pos))
|
|
} else {
|
|
Err(())
|
|
}
|
|
}
|
|
```
|
|
|
|
## D6
|
|
|
|
```rust
|
|
fn obsahuje_zaporne(cisla: &[i32]) -> bool {
|
|
cisla.iter().any(|c| *c < 0)
|
|
}
|
|
|
|
fn vsetky_neprazdne(texty: &[String]) -> bool {
|
|
texty.iter().all(|t| !t.is_empty())
|
|
}
|
|
```
|
|
|
|
## D7
|
|
|
|
```rust
|
|
fn priemer(cisla: &[f64]) -> Option<f64> {
|
|
if cisla.is_empty() {
|
|
return None;
|
|
}
|
|
Some(cisla.iter().sum::<f64>() / cisla.len() as f64)
|
|
}
|
|
```
|
|
|
|
## D8
|
|
|
|
```rust
|
|
fn vypis_s_cislami(polozky: &[String]) {
|
|
for (i, polozka) in polozky.iter().enumerate() {
|
|
println!("{}. {}", i + 1, polozka);
|
|
}
|
|
}
|
|
```
|
|
|
|
## D9
|
|
|
|
`iter()` dáva `&i32` (nemeniteľné referencie). Treba `iter_mut()`:
|
|
|
|
```rust
|
|
fn zvys_vsetky(cisla: &mut Vec<i32>) {
|
|
for c in cisla.iter_mut() {
|
|
*c += 1;
|
|
}
|
|
}
|
|
```
|
|
|
|
## D10
|
|
|
|
```rust
|
|
fn pocet_dlhych_a_mien(mena: &[String]) -> usize {
|
|
mena.iter().filter(|m| m.starts_with('A') && m.len() > 3).count()
|
|
}
|
|
```
|
|
|
|
## E1
|
|
|
|
```rust
|
|
let cisla = vec![1, 2, 3];
|
|
// 1. plný
|
|
let a: Vec<i32> = cisla.iter().map(|x: &i32| -> i32 { x * 2 }).collect();
|
|
// 2. skrátený
|
|
let b: Vec<i32> = cisla.iter().map(|x| x * 2).collect();
|
|
// 3. najkratší — tu sa nedá skrátiť viac, ale pri jednoduchej operácii:
|
|
let c: Vec<i32> = cisla.iter().map(|x| x * 2).collect();
|
|
```
|
|
|
|
## E2
|
|
|
|
```rust
|
|
let slova = vec!["ahoj".to_string(), "svet".to_string(), "a".to_string()];
|
|
|
|
// 1. closure
|
|
let dlhe: Vec<&String> = slova.iter().filter(|s| s.len() > 1).collect();
|
|
|
|
// 2. funkcia
|
|
fn je_dlhe(s: &&String) -> bool {
|
|
s.len() > 1
|
|
}
|
|
let dlhe: Vec<&String> = slova.iter().filter(je_dlhe).collect();
|
|
```
|
|
|
|
## E3
|
|
|
|
Treba `move` — closure prežije funkciu, ale `meno` je lokálna premenná:
|
|
|
|
```rust
|
|
fn vytvor_pozdrav(meno: String) -> impl Fn() -> String {
|
|
move || format!("Ahoj, {}!", meno)
|
|
}
|
|
```
|
|
|
|
## E4
|
|
|
|
```rust
|
|
fn main() {
|
|
let mut cisla = vec![1, 2, 3];
|
|
let mut pridaj = |x: i32| cisla.push(x);
|
|
pridaj(4);
|
|
pridaj(5);
|
|
pridaj(6);
|
|
// pozor: println! nemôže byť pred posledným volaním pridaj
|
|
// lebo closure drží &mut cisla
|
|
println!("{:?}", cisla); // [1, 2, 3, 4, 5, 6]
|
|
}
|
|
```
|
|
|
|
Pozn.: Ak by si chcel `println!` medzi volaniami, musel by si dropiť closure alebo použiť iný prístup.
|
|
|
|
## F1
|
|
|
|
```rust
|
|
struct Zamestnanec {
|
|
meno: String,
|
|
plat: u32,
|
|
}
|
|
|
|
struct Firma {
|
|
zamestnanci: Vec<Zamestnanec>,
|
|
}
|
|
|
|
impl Firma {
|
|
fn new() -> Firma {
|
|
Firma { zamestnanci: Vec::new() }
|
|
}
|
|
|
|
fn pridaj(&mut self, z: Zamestnanec) -> Result<(), ()> {
|
|
if self.zamestnanci.iter().any(|x| x.meno == z.meno) {
|
|
return Err(());
|
|
}
|
|
self.zamestnanci.push(z);
|
|
Ok(())
|
|
}
|
|
|
|
fn odstran(&mut self, meno: &str) -> Result<Zamestnanec, ()> {
|
|
let pos = self.zamestnanci.iter().position(|z| z.meno == meno).ok_or(())?;
|
|
Ok(self.zamestnanci.remove(pos))
|
|
}
|
|
|
|
fn najdi(&self, meno: &str) -> Option<&Zamestnanec> {
|
|
self.zamestnanci.iter().find(|z| z.meno == meno)
|
|
}
|
|
|
|
fn s_platom_nad(&self, min: u32) -> Vec<&Zamestnanec> {
|
|
self.zamestnanci.iter().filter(|z| z.plat > min).collect()
|
|
}
|
|
|
|
fn priemerny_plat(&self) -> Option<f64> {
|
|
if self.zamestnanci.is_empty() {
|
|
return None;
|
|
}
|
|
let sucet: u32 = self.zamestnanci.iter().map(|z| z.plat).sum();
|
|
Some(sucet as f64 / self.zamestnanci.len() as f64)
|
|
}
|
|
|
|
fn najlepsi_plat(&self) -> Option<&Zamestnanec> {
|
|
self.zamestnanci.iter().max_by_key(|z| z.plat)
|
|
}
|
|
}
|
|
```
|
|
|
|
## F2
|
|
|
|
```rust
|
|
use serde::{Serialize, Deserialize};
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
struct Zamestnanec { meno: String, plat: u32 }
|
|
|
|
#[derive(Serialize, Deserialize, Default)]
|
|
struct Firma { zamestnanci: Vec<Zamestnanec> }
|
|
|
|
impl Firma {
|
|
fn nacitaj_zo_suboru(cesta: &std::path::PathBuf) -> Option<Firma> {
|
|
let raw = std::fs::read_to_string(cesta).ok()?;
|
|
serde_json::from_str(&raw).ok()
|
|
}
|
|
|
|
fn uloz_do_suboru(&self, cesta: &std::path::PathBuf) -> bool {
|
|
let Ok(json) = serde_json::to_string_pretty(&self) else {
|
|
return false;
|
|
};
|
|
std::fs::write(cesta, json).is_ok()
|
|
}
|
|
}
|
|
```
|
|
|
|
## F3
|
|
|
|
```rust
|
|
fn main() {
|
|
let args: Vec<String> = std::env::args().collect();
|
|
if args.len() < 2 {
|
|
eprintln!("Použi: pridaj <meno> <plat> | odstran <meno> | zoznam");
|
|
return;
|
|
}
|
|
match args[1].as_str() {
|
|
"pridaj" => {
|
|
if args.len() < 4 {
|
|
eprintln!("Použi: pridaj <meno> <plat>");
|
|
return;
|
|
}
|
|
let meno = &args[2];
|
|
let plat: u32 = match args[3].parse() {
|
|
Ok(p) => p,
|
|
Err(_) => { eprintln!("Neplatný plat"); return; }
|
|
};
|
|
println!("Pridávam: {} s platom {}", meno, plat);
|
|
}
|
|
"odstran" => {
|
|
if args.len() < 3 {
|
|
eprintln!("Použi: odstran <meno>");
|
|
return;
|
|
}
|
|
println!("Odstraňujem: {}", args[2]);
|
|
}
|
|
"zoznam" => {
|
|
println!("Zobrazujem zoznam...");
|
|
}
|
|
iny => eprintln!("Neznámy príkaz: {}", iny),
|
|
}
|
|
}
|
|
```
|
|
|
|
## F4
|
|
|
|
```rust
|
|
// lib.rs
|
|
use serde::{Serialize, Deserialize};
|
|
use std::collections::HashMap;
|
|
|
|
#[derive(Serialize, Deserialize, Clone)]
|
|
pub struct Slovo {
|
|
pub original: String,
|
|
pub preklad: String,
|
|
pub kategoria: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Default)]
|
|
pub struct Slovnik {
|
|
pub slova: Vec<Slovo>,
|
|
}
|
|
|
|
impl Slovnik {
|
|
pub fn nacitaj_zo_suboru(cesta: &std::path::PathBuf) -> Option<Slovnik> {
|
|
let raw = std::fs::read_to_string(cesta).ok()?;
|
|
serde_json::from_str(&raw).ok()
|
|
}
|
|
|
|
pub fn uloz_do_suboru(&self, cesta: &std::path::PathBuf) -> bool {
|
|
let Ok(json) = serde_json::to_string_pretty(&self) else {
|
|
return false;
|
|
};
|
|
std::fs::write(cesta, json).is_ok()
|
|
}
|
|
|
|
pub fn pridaj_slovo(&mut self, slovo: Slovo) -> Result<(), ()> {
|
|
if self.slova.iter().any(|s| s.original == slovo.original) {
|
|
return Err(());
|
|
}
|
|
self.slova.push(slovo);
|
|
Ok(())
|
|
}
|
|
|
|
pub fn odstran_slovo(&mut self, original: &str) -> Result<Slovo, ()> {
|
|
let pos = self.slova.iter().position(|s| s.original == original).ok_or(())?;
|
|
Ok(self.slova.remove(pos))
|
|
}
|
|
|
|
pub fn preloz(&self, original: &str) -> Option<&str> {
|
|
self.slova.iter()
|
|
.find(|s| s.original == original)
|
|
.map(|s| s.preklad.as_str())
|
|
}
|
|
|
|
pub fn slova_v_kategorii(&self, kat: &str) -> Vec<&Slovo> {
|
|
self.slova.iter().filter(|s| s.kategoria == kat).collect()
|
|
}
|
|
|
|
pub fn pocet_v_kategoriach(&self) -> HashMap<String, usize> {
|
|
let mut mapa = HashMap::new();
|
|
for slovo in &self.slova {
|
|
*mapa.entry(slovo.kategoria.clone()).or_insert(0) += 1;
|
|
}
|
|
mapa
|
|
}
|
|
}
|
|
```
|
|
|
|
## G1
|
|
|
|
```rust
|
|
use std::collections::HashMap;
|
|
|
|
fn pocitaj_znaky(text: &str) -> HashMap<char, usize> {
|
|
let mut mapa = HashMap::new();
|
|
for c in text.chars() {
|
|
*mapa.entry(c).or_insert(0) += 1;
|
|
}
|
|
mapa
|
|
}
|
|
```
|
|
|
|
## G2
|
|
|
|
```rust
|
|
fn zdvojnasob_ak_existuje(x: Option<i32>) -> Option<i32> {
|
|
x.map(|n| n * 2)
|
|
}
|
|
```
|
|
|
|
## G3
|
|
|
|
```rust
|
|
fn parsuj_option(maybe_text: Option<String>) -> Option<i32> {
|
|
maybe_text.and_then(|t| t.parse::<i32>().ok())
|
|
}
|
|
```
|
|
|
|
## G4
|
|
|
|
```rust
|
|
fn nacitaj(cesta: &str) -> Result<String, String> {
|
|
std::fs::read_to_string(cesta).map_err(|e| e.to_string())
|
|
}
|
|
```
|
|
|
|
## G5
|
|
|
|
```
|
|
Some(5)
|
|
None
|
|
None
|
|
```
|