diff --git a/priprava/rust_priprava12.md b/priprava/rust_priprava12.md new file mode 100644 index 0000000..5d70fff --- /dev/null +++ b/priprava/rust_priprava12.md @@ -0,0 +1,1133 @@ +# 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<(), ()>`