diff --git a/JR-priprava-na-skusku6/Cargo.lock b/JR-priprava-na-skusku6/Cargo.lock index b60111a..d923f33 100644 --- a/JR-priprava-na-skusku6/Cargo.lock +++ b/JR-priprava-na-skusku6/Cargo.lock @@ -7,8 +7,21 @@ name = "JR-priprava-na-skusku6" version = "0.1.0" dependencies = [ "serde", + "serde_json", ] +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + [[package]] name = "proc-macro2" version = "1.0.106" @@ -57,6 +70,19 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + [[package]] name = "syn" version = "2.0.115" @@ -73,3 +99,9 @@ name = "unicode-ident" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/JR-priprava-na-skusku6/Cargo.toml b/JR-priprava-na-skusku6/Cargo.toml index df68c66..8d9af04 100644 --- a/JR-priprava-na-skusku6/Cargo.toml +++ b/JR-priprava-na-skusku6/Cargo.toml @@ -5,3 +5,4 @@ edition = "2024" [dependencies] serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.149" diff --git a/JR-priprava-na-skusku6/src/lib.rs b/JR-priprava-na-skusku6/src/lib.rs index 04f14f3..b9e69a7 100644 --- a/JR-priprava-na-skusku6/src/lib.rs +++ b/JR-priprava-na-skusku6/src/lib.rs @@ -1,6 +1,7 @@ use serde::Deserialize; use serde::Serialize; use std::fmt; +use std::fs; #[derive(Default, Serialize, Deserialize, PartialEq, Clone)] pub enum Zaner { @@ -69,3 +70,19 @@ impl fmt::Display for Stav { } } } + +impl Filmoteka { + pub fn nacitaj_zo_suboru(cesta: &std::path::PathBuf) -> Option { + let raw = fs::read_to_string(cesta).ok()?; + let result = serde_json::from_str(&raw).ok(); + result + } + + pub fn uloz_do_suboru(&self, cesta: &std::path::PathBuf) -> bool { + let Ok(json) = serde_json::to_string_pretty(&self) else { + return false; + }; + fs::write(cesta, json).is_ok() + } + +} diff --git a/priprava/rust_priprava2.md b/priprava/rust_priprava2.md new file mode 100644 index 0000000..001871d --- /dev/null +++ b/priprava/rust_priprava2.md @@ -0,0 +1,1075 @@ +# 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` = operácia, ktorá môže zlyhať (súbor, parsing, sieť) +`Option` = 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 = Ok(5); + let b: Result = 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 = Some(10); + let d: Option = 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 { + std::fs::read_to_string(cesta) +} +``` + +Napíš 3 rôzne funkcie, každá spracuje výsledok inak: + +1. `pouzij_ok()` — vráti `Option` (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` ktorá: +1. Vezme `text` +2. Skúsi ho sparsovať na `i32` (`text.parse::()` vracia `Result`) +3. Vráti `Option` + +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 = Some(5); + let b: Option = None; + let c: Result = 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 = 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> { + 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::() { + 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 { + 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>` ktorá: +1. Prečíta súbor (Result) → ak zlyhá, vráti None +2. Deserializuje JSON na `Vec` (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` 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 { + 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, meno: &str) -> Result`: +- 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`: +- Ak je prázdny slice → None +- Inak vráť priemer + +Použi `.iter().sum::()` 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) { + for c in cisla.iter() { + *c += 1; + } +} +``` + +### D10. Reťazenie iterátorov + +Napíš jedným výrazom (reťazením): +Máš `Vec` 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` 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, +} + +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 { /* */ } + + // 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 { /* */ } + + // 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 +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 = 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 + + Serialize, Deserialize, Default + +Metódy: +- nacitaj_zo_suboru(cesta) -> Option +- uloz_do_suboru(cesta) -> bool +- pridaj_slovo(slovo) -> Result<(), ()> (duplicita podľa original) +- odstran_slovo(original: &str) -> Result +- preloz(original: &str) -> Option<&str> (vráti preklad) +- slova_v_kategorii(kat: &str) -> Vec<&Slovo> +- pocet_v_kategoriach() -> HashMap +``` + +--- + +## Časť G: Špeciálne vzory + +### G1. or_insert / entry API + +Napíš funkciu `pocitaj_znaky(text: &str) -> HashMap`: +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) -> Option { + match x { + Some(n) => Some(n * 2), + None => None, + } +} +``` + +### G3. Option::and_then (flatmap) + +Napíš funkciu: +```rust +// Máš Option. Ak Some, skús parsovať na i32. Ak sa podarí, vráť Some(i32). Inak None. +fn parsuj_option(maybe_text: Option) -> Option +``` +Použi `.and_then()`. Jeden riadok. + +### G4. Result::map_err + +Napíš funkciu: +```rust +// Konvertuj io::Error na String +fn nacitaj(cesta: &str) -> Result { + // std::fs::read_to_string vracia Result + // ty chceš Result +} +``` +Použi `.map_err()`. + +### G5. .flatten() + +Čo vráti? +```rust +let a: Option> = Some(Some(5)); +let b: Option> = Some(None); +let c: Option> = 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 { + 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 { + text.parse::().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> { + let obsah = std::fs::read_to_string(cesta)?; + let cislo = obsah.trim().parse::()?; + Ok(cislo) +} +``` + +## B2 + +```rust +fn prvy_sused(vektor: &[i32], index: usize) -> Option { + let i = index.checked_sub(1)?; + Some(*vektor.get(i)?) +} +``` + +## B3 + +```rust +fn nacitaj_json(cesta: &str) -> Option> { + 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> { + 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 { + let Ok(cislo) = text.parse::() else { + return None; + }; + if cislo > 100 { + return None; + } + Some(format!("Číslo: {}", cislo)) +} + +// S ? a filter: +fn spracuj(text: &str) -> Option { + let cislo = text.parse::().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 { + cisla.iter().map(|c| c * 2).collect() +} +``` + +## D5 + +```rust +fn odstran_podla_mena(zoznam: &mut Vec, meno: &str) -> Result { + 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, meno: &str) -> Result { + 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 { + if cisla.is_empty() { + return None; + } + Some(cisla.iter().sum::() / 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) { + 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 = cisla.iter().map(|x: &i32| -> i32 { x * 2 }).collect(); +// 2. skrátený +let b: Vec = cisla.iter().map(|x| x * 2).collect(); +// 3. najkratší — tu sa nedá skrátiť viac, ale pri jednoduchej operácii: +let c: Vec = 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, +} + +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 { + 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 { + 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 } + +impl Firma { + fn nacitaj_zo_suboru(cesta: &std::path::PathBuf) -> Option { + 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 = std::env::args().collect(); + if args.len() < 2 { + eprintln!("Použi: pridaj | odstran | zoznam"); + return; + } + match args[1].as_str() { + "pridaj" => { + if args.len() < 4 { + eprintln!("Použi: pridaj "); + 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 "); + 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, +} + +impl Slovnik { + pub fn nacitaj_zo_suboru(cesta: &std::path::PathBuf) -> Option { + 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 { + 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 { + 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 { + 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) -> Option { + x.map(|n| n * 2) +} +``` + +## G3 + +```rust +fn parsuj_option(maybe_text: Option) -> Option { + maybe_text.and_then(|t| t.parse::().ok()) +} +``` + +## G4 + +```rust +fn nacitaj(cesta: &str) -> Result { + std::fs::read_to_string(cesta).map_err(|e| e.to_string()) +} +``` + +## G5 + +``` +Some(5) +None +None +```