# impl, trait, impl Trait for — krok za krokom Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej. --- ## Kapitola 1: impl blok — metódy a asociované funkcie `impl` blok pridáva funkcie ku štruktúre alebo enumu. ```rust struct Pes { meno: String, vek: u8, } impl Pes { // Toto sú funkcie, ktoré patria k Pes } ``` Dva typy vecí v impl bloku: ### Asociovaná funkcia — nemá self Volá sa cez `Typ::nazov()`. Najčastejšie `new`: ```rust impl Pes { fn new(meno: &str, vek: u8) -> Pes { Pes { meno: meno.to_string(), vek, } } } // Volanie: let rex = Pes::new("Rex", 5); ``` Nemá `self` → nie je viazaná na konkrétnu inštanciu → voláš cez `Pes::new()`. ### Metóda — má self Volá sa cez `instancia.nazov()`: ```rust impl Pes { fn stekaj(&self) { println!("{} štekol!", self.meno); } } // Volanie: let rex = Pes::new("Rex", 5); rex.stekaj(); ``` Má `&self` → viazaná na inštanciu → voláš cez `rex.stekaj()`. ### 3 varianty self ```rust impl Pes { // &self — len čítanie, inštancia žije ďalej fn meno(&self) -> &str { &self.meno } // &mut self — mení dáta, inštancia žije ďalej fn premenuj(&mut self, nove_meno: &str) { self.meno = nove_meno.to_string(); } // self — skonzumuje inštanciu, po volaní neexistuje fn rozluc_sa(self) -> String { format!("Zbohom, {}!", self.meno) // po tomto rex neexistuje } } ``` | Signatúra | Čo môžeš | Inštancia po volaní | |-----------|----------|---------------------| | `&self` | čítať | žije ďalej | | `&mut self` | čítať + meniť | žije ďalej | | `self` | čítať + meniť + konzumovať | **neexistuje** | ### Kedy let vs let mut ```rust let rex = Pes::new("Rex", 5); rex.stekaj(); // OK — &self, len čítame rex.meno(); // OK — &self // rex.premenuj("Fido"); // CHYBA! premenuj je &mut self, ale rex nie je mut let mut rex = Pes::new("Rex", 5); rex.premenuj("Fido"); // OK — &mut self a rex je mut ``` ### Viacero impl blokov ```rust // Úplne legálne — Rust ich spojí impl Pes { fn new(meno: &str, vek: u8) -> Pes { /* ... */ } } impl Pes { fn stekaj(&self) { /* ... */ } } ``` ### Úlohy 1 **1a.** Napíš štruktúru `Obdlznik { sirka: f64, vyska: f64 }` a impl blok s: - `new(sirka, vyska) -> Obdlznik` (asociovaná funkcia) - `obsah(&self) -> f64` - `obvod(&self) -> f64` - `je_stvorec(&self) -> bool` - `zvacsit(&mut self, faktor: f64)` — vynásob obe strany **1b.** Napíš štruktúru `Pocitadlo { hodnota: i32 }` s: - `new() -> Pocitadlo` (začni na 0) - `zvys(&mut self)` - `zniz(&mut self)` - `resetuj(&mut self)` - `hodnota(&self) -> i32` Zavolaj každú metódu a over výsledok. **1c.** Čo je zle? ```rust struct Bod { x: f64, y: f64 } impl Bod { fn posun(&mut self, dx: f64, dy: f64) { self.x += dx; self.y += dy; } } fn main() { let b = Bod { x: 0.0, y: 0.0 }; b.posun(1.0, 2.0); } ``` **1d.** Čo je rozdiel? Kedy ktoré použiješ? ```rust Pes::new("Rex", 5) // ??? rex.stekaj() // ??? ``` --- ## Kapitola 2: Metódy vracajúce referencie Metódy často vracajú referencie na interné dáta: ```rust struct Osoba { meno: String, zaujmy: Vec, } impl Osoba { // Vráť referenciu na String — volajúci nemôže meniť fn meno(&self) -> &str { &self.meno } // Vráť referenciu na Vec — volajúci nemôže meniť fn zaujmy(&self) -> &Vec { &self.zaujmy } // Vráť kópiu — volajúci dostane vlastnú hodnotu fn meno_owned(&self) -> String { self.meno.clone() } } ``` ### Vracanie Option s referenciou ```rust struct Skupina { clenovia: Vec, } impl Skupina { // Nájdi člena — vráť referenciu zabalenú v Option fn najdi(&self, meno: &str) -> Option<&String> { self.clenovia.iter().find(|c| c.as_str() == meno) } // Nájdi index a odstráň — &mut self lebo meníme Vec fn odstran(&mut self, meno: &str) -> Result { let pos = self.clenovia.iter() .position(|c| c == meno) .ok_or(())?; Ok(self.clenovia.remove(pos)) } } ``` ### Vracanie Vec referencií ```rust impl Skupina { // Filtrovanie — vráť Vec referencií fn zacinajuce_na(&self, pismeno: char) -> Vec<&String> { self.clenovia.iter() .filter(|c| c.starts_with(pismeno)) .collect() } } ``` ### Úlohy 2 **2a.** Máš: ```rust struct Kniznica { knihy: Vec } struct Kniha { nazov: String, autor: String, rok: u16 } ``` Napíš: - `najdi_podla_nazvu(&self, nazov: &str) -> Option<&Kniha>` - `knihy_autora(&self, autor: &str) -> Vec<&Kniha>` - `odstran(&mut self, nazov: &str) -> Result` - `najnovsia(&self) -> Option<&Kniha>` **2b.** Prečo `najdi_podla_nazvu` vracia `Option<&Kniha>` a nie `Option`? **2c.** Prečo `odstran` vracia `Result` a nie `Result<&Kniha, ()>`? --- ## Kapitola 3: Trait — čo to je Trait je **sada metód**, ktoré typ musí implementovať. Je to ako rozhranie (interface) v iných jazykoch. ```rust // Definícia traitu — hovorí ČO treba vedieť trait Zviera { fn zvuk(&self) -> &str; fn meno(&self) -> &str; } ``` Trait hovorí: "ktokoľvek chce byť Zviera, musí vedieť `zvuk()` a `meno()`." ### Prečo traity? Bez traitov: každý typ má vlastné metódy s rôznymi názvami. ```rust // BEZ traitu — chaos impl Pes { fn stekni(&self) -> &str { "Haf!" } } impl Macka { fn maukni(&self) -> &str { "Miau!" } } impl Krava { fn buckni(&self) -> &str { "Múú!" } } // Každý má iný názov metódy — nemôžeš ich volať jednotne ``` S traitom: všetky typy majú rovnakú metódu. ```rust // S traitom — poriadok trait Zviera { fn zvuk(&self) -> &str; } // Teraz každý implementuje rovnakú metódu zvuk() // a ty ich môžeš volať jednotne ``` ### Úlohy 3 **3a.** Čo je trait vlastnými slovami? **3b.** Navrhni trait `Tvar` s metódami `obsah(&self) -> f64` a `obvod(&self) -> f64`. Len definuj trait, nič neimplementuj. **3c.** Navrhni trait `Ucet` pre bankový účet s metódami: - `zostatok(&self) -> f64` - `vloz(&mut self, suma: f64)` - `vyber(&mut self, suma: f64) -> Result<(), ()>` --- ## Kapitola 4: impl Trait for — implementácia traitu ```rust trait Zviera { fn zvuk(&self) -> &str; fn meno(&self) -> &str; } struct Pes { meno: String, } struct Macka { meno: String, } // Implementácia traitu Zviera PRE typ Pes impl Zviera for Pes { fn zvuk(&self) -> &str { "Haf!" } fn meno(&self) -> &str { &self.meno } } // Implementácia traitu Zviera PRE typ Macka impl Zviera for Macka { fn zvuk(&self) -> &str { "Miau!" } fn meno(&self) -> &str { &self.meno } } ``` Teraz obe zvieratá majú metódy `zvuk()` a `meno()`: ```rust let rex = Pes { meno: "Rex".into() }; let micka = Macka { meno: "Micka".into() }; println!("{} robí {}", rex.meno(), rex.zvuk()); // Rex robí Haf! println!("{} robí {}", micka.meno(), micka.zvuk()); // Micka robí Miau! ``` ### Trait + vlastný impl blok Štruktúra môže mať **aj** trait metódy **aj** vlastné metódy: ```rust // Trait metódy impl Zviera for Pes { fn zvuk(&self) -> &str { "Haf!" } fn meno(&self) -> &str { &self.meno } } // Vlastné metódy (nie z traitu) impl Pes { fn new(meno: &str) -> Pes { Pes { meno: meno.to_string() } } fn aport(&self) { println!("{} ide po loptičku!", self.meno); } } let rex = Pes::new("Rex"); rex.zvuk(); // z traitu rex.aport(); // vlastná metóda ``` ### Musíš implementovať VŠETKY metódy ```rust trait Zviera { fn zvuk(&self) -> &str; fn meno(&self) -> &str; } // CHYBA — chýba meno() impl Zviera for Pes { fn zvuk(&self) -> &str { "Haf!" } } // error: not all trait items implemented, missing: `meno` ``` ### Úlohy 4 **4a.** Máš trait z 3b (`Tvar` s `obsah` a `obvod`). Implementuj ho pre: - `Kruh { polomer: f64 }` — obsah = π×r², obvod = 2×π×r - `Obdlznik { a: f64, b: f64 }` — obsah = a×b, obvod = 2×(a+b) - `Trojuholnik { a: f64, b: f64, c: f64, vyska_a: f64 }` — obsah = a×vyska_a/2, obvod = a+b+c **4b.** Implementuj trait `Ucet` z 3c pre: ```rust struct SporiaciUcet { zostatok: f64 } struct BeznyUcet { zostatok: f64, limit: f64 } ``` BeznyUcet má limit prečerpania — vyber() môže ísť do mínusu až po limit. **4c.** Čo je zle? ```rust trait Pozdrav { fn pozdrav(&self) -> String; } struct Robot; impl Pozdrav for Robot { } ``` --- ## Kapitola 5: Trait s parametrom — impl Trait v argumentoch Trait môžeš použiť ako typ parametra. Funkcia potom príjme **akýkoľvek typ**, ktorý implementuje daný trait. ```rust trait Zviera { fn zvuk(&self) -> &str; fn meno(&self) -> &str; } // Funkcia príjme čokoľvek čo je Zviera fn predstav(zviera: &impl Zviera) { println!("{} robí {}", zviera.meno(), zviera.zvuk()); } let rex = Pes::new("Rex"); let micka = Macka { meno: "Micka".into() }; predstav(&rex); // OK — Pes implementuje Zviera predstav(&micka); // OK — Macka implementuje Zviera ``` ### Na skúške — IOManager pattern Na skúške je typický vzor: metóda berie `impl Trait` parameter pre I/O operácie. Toto umožňuje testovanie — na skúšku sa dá ConsoleIOManager, na testy MockIOManager. ```rust // Trait definuje AKO sa získava vstup trait IOManager { fn ziskaj_pismeno(&mut self) -> char; } // Reálna implementácia — číta z konzoly struct ConsoleIOManager; impl IOManager for ConsoleIOManager { fn ziskaj_pismeno(&mut self) -> char { let mut vstup = String::new(); std::io::stdin().read_line(&mut vstup).unwrap(); vstup.trim().chars().next().unwrap_or(' ') } } // Hra používa trait, nie konkrétny typ struct Hra { slovo: String, // ... } impl Hra { // Berie impl IOManager — funguje s akoukoľvek implementáciou fn hraj(&mut self, mut io: impl IOManager) { loop { let pismeno = io.ziskaj_pismeno(); // ... spracuj písmeno ... if self.je_koniec() { break; } } } fn je_koniec(&self) -> bool { todo!() } } // Volanie: fn main() { let mut hra = Hra { slovo: "rust".into() }; hra.hraj(ConsoleIOManager); } ``` Prečo `impl IOManager` a nie priamo `ConsoleIOManager`? - Ak napíšeš `io: ConsoleIOManager`, metóda funguje len s konzolou - Ak napíšeš `io: impl IOManager`, funguje s čímkoľvek čo implementuje IOManager - Testy môžu použiť `MockIOManager` ktorý vracia vopred určené písmená ### mut v parametri ```rust // Ak trait metóda má &mut self, parameter musí byť mut fn hraj(&mut self, mut io: impl IOManager) { // ^^^ mut lebo ziskaj_pismeno je &mut self let pismeno = io.ziskaj_pismeno(); } // Ak trait metóda má &self, netreba mut trait Zobrazovac { fn zobraz(&self, text: &str); } fn vypis(zobrazovac: &impl Zobrazovac) { zobrazovac.zobraz("ahoj"); } ``` ### Úlohy 5 **5a.** Máš trait: ```rust trait Logger { fn log(&self, sprava: &str); } ``` Implementuj ho pre: - `KonzolaLogger` — vypíše do println! - `SuborLogger { cesta: String }` — len println! s prefixom "[FILE]" - `TichyLogger` — nič nerobí Napíš funkciu `spracuj(data: &str, logger: &impl Logger)` ktorá zaloguje "Spracovávam: {data}". **5b.** Máš: ```rust trait Validator { fn je_platny(&self, vstup: &str) -> bool; } ``` Implementuj pre: - `DlzkaValidator { min: usize, max: usize }` — dĺžka v rozsahu - `EmailValidator` — obsahuje '@' a '.' Napíš funkciu `validuj(vstup: &str, validator: &impl Validator) -> bool`. **5c.** Prečo `hraj` berie `mut io: impl IOManager` a nie `io: &impl IOManager`? --- ## Kapitola 6: Returning impl Trait — vracanie impl ### impl Iterator — najčastejšie na skúške Keď metóda vracia iterátor, typ iterátora je komplikovaný. `impl Iterator` ho skryje: ```rust struct Skupina { mena: Vec, vyluceni: HashSet, } impl Skupina { // Vráť iterátor cez referencie na mená fn aktivni(&self) -> impl Iterator { self.mena.iter().filter(|m| !self.vyluceni.contains(*m)) } } ``` **Čo to znamená:** Funkcia vráti "niečo čo je Iterator a dáva &String". Volajúci nevie presný typ, ale vie iterovať. ### Konkrétne na skúške — iterátor na člen štruktúry Najčastejší vzor: vráť iterátor priamo na pole štruktúry. ```rust use std::collections::HashSet; struct Hra { hladane_slovo: String, uhadnute_pismena: HashSet, skusane_pismena: HashSet, aktualny_pocet_zivotov: u8, } impl Hra { // Vráť iterátor na uhadnute_pismena fn daj_uhadnute_pismena(&self) -> impl Iterator { self.uhadnute_pismena.iter() } // Vráť iterátor na skusane_pismena fn daj_skusane_pismena(&self) -> impl Iterator { self.skusane_pismena.iter() } } ``` Volajúci potom môže: ```rust let hra = Hra::new("rust"); // Iterovať for pismeno in hra.daj_uhadnute_pismena() { println!("{}", pismeno); } // Collect do Vec let pismena: Vec<&char> = hra.daj_uhadnute_pismena().collect(); // Count let pocet = hra.daj_skusane_pismena().count(); ``` ### Pravidlo: Item typ = čo .iter() dáva | Kolekcia | `.iter()` dáva | Item typ | |----------|---------------|----------| | `Vec` | `&String` | `Item = &String` | | `HashSet` | `&char` | `Item = &char` | | `HashMap` | `(&K, &V)` | `Item = (&K, &V)` | | `Vec` | `&Kniha` | `Item = &Kniha` | Takže: ```rust fn daj_knihy(&self) -> impl Iterator { self.knihy.iter() } fn daj_kategorie(&self) -> impl Iterator)> { self.slovnik.iter() } ``` ### impl Iterator s filtrom ```rust impl Kniznica { // Filtrovanie — stále len impl Iterator fn knihy_autora(&self, autor: &str) -> impl Iterator + '_ { self.knihy.iter().filter(move |k| k.autor == autor) } } ``` **`+ '_`** — lifecycle annotation. Treba ho ak closure v `.filter()` zachytáva parameter. Ak ti kompilér kričí o lifetimoch pri impl Iterator s filtrom, pridaj `+ '_`. **`move`** — closure zachytí `autor` hodnotou. Treba ak filter používa parameter funkcie. Pre skúšku: ak ti to robí problémy, jednoducho vráť `Vec<&Kniha>` namiesto `impl Iterator`. Je to menej elegantné, ale funguje: ```rust // Jednoduchšia alternatíva — vždy funguje, žiadne lifetime problémy fn knihy_autora(&self, autor: &str) -> Vec<&Kniha> { self.knihy.iter().filter(|k| k.autor == autor).collect() } ``` ### Úlohy 6 **6a.** Máš: ```rust struct Trieda { studenti: Vec, znamky: HashMap>, } ``` Napíš: - `daj_studentov(&self) -> impl Iterator` - `daj_znamky(&self, student: &str) -> Option<&Vec>` **6b.** Máš `struct Kosik { polozky: HashSet }`. Napíš: - `daj_polozky(&self) -> impl Iterator` **6c.** Kedy vrátit `impl Iterator` a kedy `Vec<&T>`? --- ## Kapitola 7: Display trait — impl fmt::Display Na skúške skoro vždy treba implementovať Display, aby si mohol robiť `println!("{}", instancia)`. ```rust use std::fmt; struct Kniha { nazov: String, autor: String, rok: u16, } impl fmt::Display for Kniha { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} — {} ({})", self.nazov, self.autor, self.rok) } } // Teraz funguje: let kniha = Kniha { nazov: "Duna".into(), autor: "Herbert".into(), rok: 1965 }; println!("{}", kniha); // "Duna — Herbert (1965)" ``` ### Display pre enum ```rust use std::fmt; enum Stav { Nova, Pouzivana, Poskodena, Vyradena } impl fmt::Display for Stav { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Stav::Nova => write!(f, "Nová"), Stav::Pouzivana => write!(f, "Používaná"), Stav::Poskodena => write!(f, "Poškodená"), Stav::Vyradena => write!(f, "Vyradená"), } } } // Teraz funguje: println!("{}", Stav::Nova); // "Nová" ``` ### Display pre štruktúru s enumom ```rust impl fmt::Display for Kniha { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} — {} ({}) [{}]", self.nazov, self.autor, self.rok, self.stav) // ^^^^^^^^ // stav.fmt() sa zavolá automaticky // lebo Stav implementuje Display } } println!("{}", kniha); // "Duna — Herbert (1965) [Nová]" ``` ### Display vs Debug | Trait | Macro | Derive | Účel | |-------|-------|--------|------| | `Display` | `{}` | NIE — musíš ručne | Pre používateľa | | `Debug` | `{:?}` | ÁNO — `#[derive(Debug)]` | Pre programátora | ```rust #[derive(Debug)] // Debug cez derive struct Bod { x: f64, y: f64 } impl fmt::Display for Bod { // Display ručne fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "({}, {})", self.x, self.y) } } let b = Bod { x: 1.0, y: 2.0 }; println!("{}", b); // (1, 2) — Display println!("{:?}", b); // Bod { x: 1.0, y: 2.0 } — Debug ``` ### Úlohy 7 **7a.** Implementuj Display pre: ```rust enum VysledokPokusu { Uhadnute, Neuhadnute, Neplatne } ``` Výstup: "Uhádnuté", "Neuhádnuté", "Neplatné". **7b.** Implementuj Display pre: ```rust struct Zamestnanec { meno: String, pozicia: String, plat: u32 } ``` Výstup: `"Anna (Programátor) - 3000€"` **7c.** Implementuj Display pre enum aj štruktúru: ```rust enum Priorita { Nizka, Stredna, Vysoka } struct Uloha { nazov: String, priorita: Priorita, hotova: bool } ``` Uloha výstup: `"[✓] Upratovanie (Vysoká)"` alebo `"[ ] Upratovanie (Vysoká)"` podľa hotova. --- ## Kapitola 8: Default trait — ručne `#[derive(Default)]` funguje keď chceš štandardné default hodnoty (0, "", vec![], None). Keď chceš vlastné, implementuj ručne. ```rust // derive Default — automatické #[derive(Default)] struct Config { port: u16, // 0 host: String, // "" debug: bool, // false } // Ručný Default — vlastné hodnoty struct Config2 { port: u16, host: String, debug: bool, } impl Default for Config2 { fn default() -> Self { Config2 { port: 8080, // vlastná hodnota host: "localhost".to_string(), debug: false, } } } let c = Config2::default(); // port = 8080, host = "localhost", debug = false ``` ### Default pre enum Enum nemá automatický default — musíš povedať ktorý variant: ```rust enum Stav { Nova, Pouzivana, Poskodena, Vyradena } impl Default for Stav { fn default() -> Self { Stav::Nova // Nova je default } } let s = Stav::default(); // Stav::Nova ``` ### Úlohy 8 **8a.** Implementuj Default pre: ```rust struct Nastavenia { jazyk: String, // "sk" tema: String, // "svetla" velkost_fontu: u8, // 14 zvuk: bool, // true } ``` **8b.** Implementuj Default pre enum: ```rust enum Obtiaznost { Lahka, Stredna, Tazka } ``` Default: Stredna. --- ## Kapitola 9: Viacero traitov na jednom type Jedna štruktúra môže implementovať koľko traitov chce: ```rust use std::fmt; use serde::{Serialize, Deserialize}; // Enum s veľa traitmi #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] enum Stav { Nova, Pouzivana, Poskodena, Vyradena } // Ručné traity impl fmt::Display for Stav { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Stav::Nova => write!(f, "Nová"), Stav::Pouzivana => write!(f, "Používaná"), Stav::Poskodena => write!(f, "Poškodená"), Stav::Vyradena => write!(f, "Vyradená"), } } } impl Default for Stav { fn default() -> Self { Stav::Nova } } // Teraz Stav má: Debug, Clone, PartialEq, Serialize, Deserialize, Display, Default ``` ### Štruktúra s derive + ručné traity + vlastný impl ```rust #[derive(Debug, Clone, Serialize, Deserialize)] struct Kniha { nazov: String, autor: String, stav: Stav, } // Display — ručne impl fmt::Display for Kniha { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} — {} [{}]", self.nazov, self.autor, self.stav) } } // Vlastné metódy impl Kniha { fn new(nazov: &str, autor: &str) -> Kniha { Kniha { nazov: nazov.to_string(), autor: autor.to_string(), stav: Stav::default(), } } } ``` Tri rôzne impl bloky pre jeden typ — úplne OK: 1. `#[derive(...)]` — automatické traity 2. `impl Display for Kniha` — ručný trait 3. `impl Kniha` — vlastné metódy ### Úlohy 9 **9a.** Vytvor kompletný enum `VysledokPokusu` s hodnotami `Uhadnute, Neuhadnute, Neplatne`. Pridaj: - derive: Debug, Clone, PartialEq - Display: vlastné slovenské texty Napíš 3 testy, kde porovnávaš varianty cez `==` a vypisuješ cez `{}`. **9b.** Vytvor kompletný typ pre skúšku: ```rust // Enum Stav s derive + Display + Default // Struct Kniha s derive + Display + new() // Struct Kniznica s derive + Default + nacitaj/uloz + metódy ``` Napíš len signatúry a derive/impl hlavičky — telo metód nechaj ako `todo!()`. --- ## Kapitola 10: Kompletný vzor — Obesenec zo skúšky Toto je celý vzor, ako by vyzerala skúška. Prejdi si ho riadok po riadku. ```rust use std::collections::HashSet; use std::fmt; use serde::{Serialize, Deserialize}; use std::path::PathBuf; use std::collections::HashMap; use rand::seq::SliceRandom; // ============================================================ // ENUM — derive + Display // ============================================================ #[derive(Debug, Clone, PartialEq)] pub enum VysledokPokusu { Uhadnute, Neuhadnute, Neplatne, } impl fmt::Display for VysledokPokusu { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { VysledokPokusu::Uhadnute => write!(f, "Uhádnuté"), VysledokPokusu::Neuhadnute => write!(f, "Neuhádnuté"), VysledokPokusu::Neplatne => write!(f, "Neplatné"), } } } // ============================================================ // TRAIT — definícia rozhrania // ============================================================ pub trait IOManager { fn ziskaj_pismeno(&mut self) -> char; } // ============================================================ // ŠTRUKTURA Hra — logika hry // ============================================================ pub struct Hra { hladane_slovo: String, uhadnute_pismena: HashSet, skusane_pismena: HashSet, aktualny_pocet_zivotov: u8, } // Vlastné metódy (asociované funkcie + metódy) impl Hra { // Asociovaná funkcia — Hra::new() pub fn new(slovo: &str) -> Hra { Hra { hladane_slovo: slovo.to_lowercase(), uhadnute_pismena: HashSet::new(), skusane_pismena: HashSet::new(), aktualny_pocet_zivotov: 5, } } // &mut self — mení stav hry pub fn tipni_pismeno(&mut self, pismeno: char) -> VysledokPokusu { let p = pismeno.to_lowercase().next().unwrap(); // Už bolo skúšané → Neplatné if !self.skusane_pismena.insert(p) { return VysledokPokusu::Neplatne; } // Je v slove → Uhádnuté if self.hladane_slovo.contains(p) { self.uhadnute_pismena.insert(p); VysledokPokusu::Uhadnute } else { self.aktualny_pocet_zivotov -= 1; VysledokPokusu::Neuhadnute } } // &self — len čítanie, vráti iterátor pub fn daj_uhadnute_pismena(&self) -> impl Iterator { self.uhadnute_pismena.iter() } pub fn daj_skusane_pismena(&self) -> impl Iterator { self.skusane_pismena.iter() } // &self — vracia nový String pub fn daj_slovo_skryto(&self) -> String { self.hladane_slovo.chars().map(|c| { if self.uhadnute_pismena.contains(&c) { c } else { '_' } }).collect() } pub fn daj_dlzku_slova(&self) -> usize { self.hladane_slovo.chars().count() } pub fn je_koniec_hry(&self) -> bool { self.aktualny_pocet_zivotov == 0 || self.je_vyhra() } pub fn je_vyhra(&self) -> bool { self.hladane_slovo.chars() .all(|c| self.uhadnute_pismena.contains(&c)) } // &mut self + impl IOManager parameter pub fn hraj(&mut self, mut io: impl IOManager) { while !self.je_koniec_hry() { // Vypíš stav println!("Životov: {}", self.aktualny_pocet_zivotov); println!("Slovo: {}", self.daj_slovo_skryto()); let skusane: Vec<&char> = self.daj_skusane_pismena().collect(); println!("Skúšané: {:?}", skusane); // Získaj písmeno cez IOManager trait let pismeno = io.ziskaj_pismeno(); // Tipni a vypíš výsledok let vysledok = self.tipni_pismeno(pismeno); println!("{}", vysledok); } // Koniec if self.je_vyhra() { println!("Výhra! Slovo bolo: {}", self.hladane_slovo); } else { println!("Prehra! Slovo bolo: {}", self.hladane_slovo); } } } // ============================================================ // ŠTRUKTURA SpravcaHry — serde + HashMap // ============================================================ #[derive(Serialize, Deserialize, Default)] pub struct SpravcaHry { slovnik: HashMap>, } impl SpravcaHry { // Statická — SpravcaHry::nacitaj_zo_suboru() pub fn nacitaj_zo_suboru(cesta: &PathBuf) -> Option { let raw = std::fs::read_to_string(cesta).ok()?; serde_json::from_str(&raw).ok() } // &self metóda pub fn uloz_do_suboru(&self, cesta: &PathBuf) -> bool { let Ok(json) = serde_json::to_string_pretty(&self) else { return false; }; std::fs::write(cesta, json).is_ok() } // &self — vracia Option pub fn vytvor_novu_hru(&self, kategoria: &str) -> Option { let slova = self.slovnik.get(kategoria)?; if slova.is_empty() { return None; } let mut rng = rand::thread_rng(); let slovo = slova.choose(&mut rng)?; Some(Hra::new(slovo)) } // &mut self pub fn pridaj_kategoriu(&mut self, kategoria: &str) -> Result<(), ()> { if self.slovnik.contains_key(kategoria) { return Err(()); } self.slovnik.insert(kategoria.to_string(), Vec::new()); Ok(()) } // &mut self pub fn pridaj_slovo(&mut self, kategoria: &str, slovo: &str) -> Result<(), ()> { let slova = self.slovnik.get_mut(kategoria).ok_or(())?; slova.push(slovo.to_string()); Ok(()) } } // ============================================================ // IMPL TRAIT FOR — ConsoleIOManager implementuje IOManager // ============================================================ pub struct ConsoleIOManager; impl IOManager for ConsoleIOManager { fn ziskaj_pismeno(&mut self) -> char { let mut vstup = String::new(); std::io::stdin().read_line(&mut vstup).unwrap(); vstup.trim().chars().next().unwrap_or(' ') } } ``` ### Úlohy 10 **10a.** Bez pozerania na kód vyššie, napíš: - Enum `VysledokPokusu` s derive a Display - Trait `IOManager` s metódou `ziskaj_pismeno` - Struct `ConsoleIOManager` a jeho impl IOManager **10b.** Bez pozerania, napíš struct `Hra` a tieto metódy: - `Hra::new(slovo: &str) -> Hra` - `tipni_pismeno(&mut self, pismeno: char) -> VysledokPokusu` - `daj_uhadnute_pismena(&self) -> impl Iterator` - `daj_slovo_skryto(&self) -> String` - `je_koniec_hry(&self) -> bool` **10c.** Bez pozerania, napíš struct `SpravcaHry` a: - `nacitaj_zo_suboru(cesta: &PathBuf) -> Option` - `uloz_do_suboru(&self, cesta: &PathBuf) -> bool` - `vytvor_novu_hru(&self, kategoria: &str) -> Option` - `pridaj_kategoriu(&mut self, kategoria: &str) -> Result<(), ()>`