# 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 ```