24 KiB
Rust — Základy na dril
Každé cvičenie: prečítaj, napíš kód, skompiluj, porovnaj s riešením na konci. Riešenia sú číslované na konci súboru. Nepozeraj dopredu.
Časť A: Result a Option — kedy čo
Result<T, E> = operácia, ktorá môže zlyhať (súbor, parsing, sieť)
Option<T> = hodnota, ktorá môže chýbať (hľadanie, konverzia)
A1. Konverzie medzi Result a Option
Napíš, čo vráti každý výraz. Nekompiluj, najprv tipni, potom over.
fn main() {
let a: Result<i32, &str> = Ok(5);
let b: Result<i32, &str> = Err("chyba");
println!("{:?}", a.ok()); // ?
println!("{:?}", b.ok()); // ?
println!("{:?}", a.is_ok()); // ?
println!("{:?}", b.is_ok()); // ?
println!("{:?}", a.is_err()); // ?
println!("{:?}", b.is_err()); // ?
let c: Option<i32> = Some(10);
let d: Option<i32> = None;
println!("{:?}", c.ok_or("prazdne")); // ?
println!("{:?}", d.ok_or("prazdne")); // ?
println!("{:?}", c.is_some()); // ?
println!("{:?}", d.is_none()); // ?
}
A2. .ok() vs .is_ok() — kedy ktoré
Máš funkciu:
fn nacitaj(cesta: &str) -> Result<String, std::io::Error> {
std::fs::read_to_string(cesta)
}
Napíš 3 rôzne funkcie, každá spracuje výsledok inak:
pouzij_ok()— vrátiOption<String>(zahoď chybu, zaujíma ťa len hodnota)pouzij_is_ok()— vrátibool(zaujíma ťa len či sa podarilo)pouzij_match()— vypíše obsah ak Ok, vypíše chybu ak Err
A3. Reťazenie .ok()
Napíš funkciu parsuj_cislo(text: &str) -> Option<i32> ktorá:
- Vezme
text - Skúsi ho sparsovať na
i32(text.parse::<i32>()vraciaResult) - Vráti
Option<i32>
Použi jeden riadok bez match, bez if.
A4. .unwrap_or() a .unwrap_or_default()
Napíš čo vypíše:
fn main() {
let a: Option<i32> = Some(5);
let b: Option<i32> = None;
let c: Result<i32, &str> = Err("zle");
println!("{}", a.unwrap_or(0));
println!("{}", b.unwrap_or(0));
println!("{}", b.unwrap_or_default());
println!("{}", c.unwrap_or(99));
println!("{}", c.unwrap_or_default());
let d: Option<String> = None;
println!("{:?}", d.unwrap_or_default());
println!("{:?}", d.unwrap_or("fallback".to_string()));
}
Časť B: Operátor ? (question mark)
B1. Základné použitie
Táto funkcia je napísaná s match. Prepíš ju tak, aby používala ? a bola čo najkratšia:
fn nacitaj_a_parsuj(cesta: &str) -> Result<i32, Box<dyn std::error::Error>> {
let obsah = match std::fs::read_to_string(cesta) {
Ok(s) => s,
Err(e) => return Err(Box::new(e)),
};
let cislo = match obsah.trim().parse::<i32>() {
Ok(n) => n,
Err(e) => return Err(Box::new(e)),
};
Ok(cislo)
}
B2. ? v Option kontexte
Prepíš s operátorom ? (funkcia vracia Option, nie Result):
fn prvy_sused(vektor: &[i32], index: usize) -> Option<i32> {
if index == 0 {
return None;
}
let predchadzajuci = index.checked_sub(1);
match predchadzajuci {
Some(i) => {
if i < vektor.len() {
Some(vektor[i])
} else {
None
}
}
None => None,
}
}
B3. .ok()? vzor
Napíš funkciu nacitaj_json(cesta: &str) -> Option<Vec<String>> ktorá:
- Prečíta súbor (Result) → ak zlyhá, vráti None
- Deserializuje JSON na
Vec<String>(Result) → ak zlyhá, vráti None - 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):
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.
// 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č.
// Ktoré je lepšie — if let alebo match?
Napíš obe verzie.
Situácia 3: Chceš rôzne akcie pre Ok a Err.
// Ktoré je lepšie — match, if let, alebo let...else?
Napíš všetky 3, rozhodni sa.
C2. let...else v praxi
Napíš funkciu spracuj(text: &str) -> Option<String> ktorá:
- Skúsi sparsovať
textnau32. Ak sa nepodarí → vráť None. - Ak číslo > 100, vráť None.
- Vráť
Some(format!("Číslo: {}", cislo))
Napíš to dvakrát:
- Raz s
let...else - Raz bez
let...else(s?alebomatch)
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):
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:
fn zdvojnasob(cisla: &[i32]) -> Vec<i32> {
let mut vysledok = Vec::new();
for c in cisla {
vysledok.push(c * 2);
}
vysledok
}
D5. position (na remove z Vec)
Napíš funkciu odstran_podla_mena(zoznam: &mut Vec<String>, meno: &str) -> Result<String, ()>:
- Nájdi index prvku s daným menom
- Odstráň ho z vektora a vráť ho
- Ak neexistuje → Err(())
Použi .position() a .remove().
D6. any / all
Bez cyklu napíš:
obsahuje_zaporne(cisla: &[i32]) -> bool— true ak aspoň jedno < 0vsetky_neprazdne(texty: &[String]) -> bool— true ak žiadny nie je prázdny
D7. fold / sum
Napíš priemer(cisla: &[f64]) -> Option<f64>:
- Ak je prázdny slice → None
- Inak vráť priemer
Použi .iter().sum::<f64>() alebo .fold().
D8. enumerate
Napíš funkciu vypis_s_cislami(polozky: &[String]) ktorá vypíše:
1. prvá položka
2. druhá položka
Použi .enumerate().
D9. iter() vs into_iter() vs iter_mut()
Čo je zle na tomto kóde? Oprav:
fn zvys_vsetky(cisla: &mut Vec<i32>) {
for c in cisla.iter() {
*c += 1;
}
}
D10. Reťazenie iterátorov
Napíš jedným výrazom (reťazením):
Máš Vec<String> mien. Chceš nájsť počet mien, ktoré začínajú na 'A' a sú dlhšie ako 3 znaky.
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:
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:
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:
fn vytvor_pozdrav(meno: String) -> impl Fn() -> String {
let closure = || format!("Ahoj, {}!", meno);
closure
}
E4. Closure zachytávajúci &mut
Napíš kód, ktorý:
- Má
Vec<i32>s hodnotami [1, 2, 3] - Vytvorí closure, ktorý pridá číslo do vektora
- Zavolaj closure 3x s hodnotami 4, 5, 6
- 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):
struct Zamestnanec {
meno: String,
plat: u32,
}
struct Firma {
zamestnanci: Vec<Zamestnanec>,
}
impl Firma {
fn new() -> Firma { /* */ }
// Pridaj zamestnanca. Ak meno existuje → Err(())
fn pridaj(&mut self, z: Zamestnanec) -> Result<(), ()> { /* */ }
// Odstráň podľa mena. Vráť odstranenéno. Ak neexistuje → Err(())
fn odstran(&mut self, meno: &str) -> Result<Zamestnanec, ()> { /* */ }
// Nájdi podľa mena
fn najdi(&self, meno: &str) -> Option<&Zamestnanec> { /* */ }
// Všetci s platom nad X
fn s_platom_nad(&self, min: u32) -> Vec<&Zamestnanec> { /* */ }
// Priemerný plat. None ak nikto.
fn priemerny_plat(&self) -> Option<f64> { /* */ }
// Najdi najlepsie plateneho
fn najlepsi_plat(&self) -> Option<&Zamestnanec> { /* */ }
}
F2. File I/O kombinácia
Napíš obe funkcie pre Firma (s derive Serialize, Deserialize):
fn nacitaj_zo_suboru(cesta: &std::path::PathBuf) -> Option<Firma>
fn uloz_do_suboru(&self, cesta: &std::path::PathBuf) -> bool
F3. CLI parsovanie vstupu
Napíš funkciu v main() (bez clap), ktorá:
- Prečíta argumenty z príkazového riadku (
std::env::args()) - Prvý argument je príkaz: "pridaj", "odstran", "zoznam"
- Pre "pridaj": ďalšie 2 argumenty sú meno a plat
- Pre "odstran": ďalší argument je meno
- Pre "zoznam": žiadne ďalšie argumenty
Použi let args: Vec<String> = std::env::args().collect(); a potom match/if na args[1].
F4. Všetko dokopy
Napíš kompletný program (lib.rs + main.rs) pre jednoduchý Slovník (prekladový):
Štruktúra Slovo: original (String), preklad (String), kategoria (String)
Štruktúra Slovnik: slova: Vec<Slovo>
+ Serialize, Deserialize, Default
Metódy:
- nacitaj_zo_suboru(cesta) -> Option<Slovnik>
- uloz_do_suboru(cesta) -> bool
- pridaj_slovo(slovo) -> Result<(), ()> (duplicita podľa original)
- odstran_slovo(original: &str) -> Result<Slovo, ()>
- preloz(original: &str) -> Option<&str> (vráti preklad)
- slova_v_kategorii(kat: &str) -> Vec<&Slovo>
- pocet_v_kategoriach() -> HashMap<String, usize>
Časť G: Špeciálne vzory
G1. or_insert / entry API
Napíš funkciu pocitaj_znaky(text: &str) -> HashMap<char, usize>:
Počíta výskyt každého znaku v texte. Použi .entry().or_insert(0).
G2. Option::map
Prepíš bez match/if:
fn zdvojnasob_ak_existuje(x: Option<i32>) -> Option<i32> {
match x {
Some(n) => Some(n * 2),
None => None,
}
}
G3. Option::and_then (flatmap)
Napíš funkciu:
// Máš Option<String>. Ak Some, skús parsovať na i32. Ak sa podarí, vráť Some(i32). Inak None.
fn parsuj_option(maybe_text: Option<String>) -> Option<i32>
Použi .and_then(). Jeden riadok.
G4. Result::map_err
Napíš funkciu:
// Konvertuj io::Error na String
fn nacitaj(cesta: &str) -> Result<String, String> {
// std::fs::read_to_string vracia Result<String, io::Error>
// ty chceš Result<String, String>
}
Použi .map_err().
G5. .flatten()
Čo vráti?
let a: Option<Option<i32>> = Some(Some(5));
let b: Option<Option<i32>> = Some(None);
let c: Option<Option<i32>> = None;
println!("{:?}", a.flatten());
println!("{:?}", b.flatten());
println!("{:?}", c.flatten());
RIEŠENIA
A1
Some(5)
None
true
false
false
true
Ok(10)
Err("prazdne")
true
true
A2
fn pouzij_ok(cesta: &str) -> Option<String> {
nacitaj(cesta).ok()
}
fn pouzij_is_ok(cesta: &str) -> bool {
nacitaj(cesta).is_ok()
}
fn pouzij_match(cesta: &str) {
match nacitaj(cesta) {
Ok(obsah) => println!("{}", obsah),
Err(e) => println!("Chyba: {}", e),
}
}
A3
fn parsuj_cislo(text: &str) -> Option<i32> {
text.parse::<i32>().ok()
}
A4
5
0
0
99
0
""
"fallback"
Pozn.: unwrap_or_default() — pre i32 default je 0, pre String je "".
B1
fn nacitaj_a_parsuj(cesta: &str) -> Result<i32, Box<dyn std::error::Error>> {
let obsah = std::fs::read_to_string(cesta)?;
let cislo = obsah.trim().parse::<i32>()?;
Ok(cislo)
}
B2
fn prvy_sused(vektor: &[i32], index: usize) -> Option<i32> {
let i = index.checked_sub(1)?;
Some(*vektor.get(i)?)
}
B3
fn nacitaj_json(cesta: &str) -> Option<Vec<String>> {
let raw = std::fs::read_to_string(cesta).ok()?;
serde_json::from_str(&raw).ok()
}
B4
main() vracia (), nie Result. Operátor ? sa nedá použiť vo funkcii, ktorá nevracia Result alebo Option.
Oprava 1 — main vracia Result:
fn main() -> Result<(), Box<dyn std::error::Error>> {
let obsah = std::fs::read_to_string("subor.txt")?;
println!("{}", obsah);
Ok(())
}
Oprava 2 — match/unwrap:
fn main() {
match std::fs::read_to_string("subor.txt") {
Ok(obsah) => println!("{}", obsah),
Err(e) => eprintln!("Chyba: {}", e),
}
}
C1
Situácia 1:
// 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:
// 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:
// 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
// S let...else:
fn spracuj(text: &str) -> Option<String> {
let Ok(cislo) = text.parse::<u32>() else {
return None;
};
if cislo > 100 {
return None;
}
Some(format!("Číslo: {}", cislo))
}
// S ? a filter:
fn spracuj(text: &str) -> Option<String> {
let cislo = text.parse::<u32>().ok()?;
if cislo > 100 {
return None;
}
Some(format!("Číslo: {}", cislo))
}
C3
// 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
fn pocet_kladnych(cisla: &[i32]) -> usize {
cisla.iter().filter(|c| **c > 0).count()
}
D2
fn dlhe_slova(slova: &[String], min_dlzka: usize) -> Vec<&String> {
slova.iter().filter(|s| s.len() > min_dlzka).collect()
}
D3
fn najdi_podla_zaciatku<'a>(slova: &'a [String], prefix: &str) -> Option<&'a String> {
slova.iter().find(|s| s.starts_with(prefix))
}
D4
fn zdvojnasob(cisla: &[i32]) -> Vec<i32> {
cisla.iter().map(|c| c * 2).collect()
}
D5
fn odstran_podla_mena(zoznam: &mut Vec<String>, meno: &str) -> Result<String, ()> {
let pos = zoznam.iter().position(|s| s == meno).ok_or(())?;
Ok(zoznam.remove(pos))
}
Alternatíva bez ?:
fn odstran_podla_mena(zoznam: &mut Vec<String>, meno: &str) -> Result<String, ()> {
if let Some(pos) = zoznam.iter().position(|s| s == meno) {
Ok(zoznam.remove(pos))
} else {
Err(())
}
}
D6
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
fn priemer(cisla: &[f64]) -> Option<f64> {
if cisla.is_empty() {
return None;
}
Some(cisla.iter().sum::<f64>() / cisla.len() as f64)
}
D8
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():
fn zvys_vsetky(cisla: &mut Vec<i32>) {
for c in cisla.iter_mut() {
*c += 1;
}
}
D10
fn pocet_dlhych_a_mien(mena: &[String]) -> usize {
mena.iter().filter(|m| m.starts_with('A') && m.len() > 3).count()
}
E1
let cisla = vec![1, 2, 3];
// 1. plný
let a: Vec<i32> = cisla.iter().map(|x: &i32| -> i32 { x * 2 }).collect();
// 2. skrátený
let b: Vec<i32> = cisla.iter().map(|x| x * 2).collect();
// 3. najkratší — tu sa nedá skrátiť viac, ale pri jednoduchej operácii:
let c: Vec<i32> = cisla.iter().map(|x| x * 2).collect();
E2
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á:
fn vytvor_pozdrav(meno: String) -> impl Fn() -> String {
move || format!("Ahoj, {}!", meno)
}
E4
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
struct Zamestnanec {
meno: String,
plat: u32,
}
struct Firma {
zamestnanci: Vec<Zamestnanec>,
}
impl Firma {
fn new() -> Firma {
Firma { zamestnanci: Vec::new() }
}
fn pridaj(&mut self, z: Zamestnanec) -> Result<(), ()> {
if self.zamestnanci.iter().any(|x| x.meno == z.meno) {
return Err(());
}
self.zamestnanci.push(z);
Ok(())
}
fn odstran(&mut self, meno: &str) -> Result<Zamestnanec, ()> {
let pos = self.zamestnanci.iter().position(|z| z.meno == meno).ok_or(())?;
Ok(self.zamestnanci.remove(pos))
}
fn najdi(&self, meno: &str) -> Option<&Zamestnanec> {
self.zamestnanci.iter().find(|z| z.meno == meno)
}
fn s_platom_nad(&self, min: u32) -> Vec<&Zamestnanec> {
self.zamestnanci.iter().filter(|z| z.plat > min).collect()
}
fn priemerny_plat(&self) -> Option<f64> {
if self.zamestnanci.is_empty() {
return None;
}
let sucet: u32 = self.zamestnanci.iter().map(|z| z.plat).sum();
Some(sucet as f64 / self.zamestnanci.len() as f64)
}
fn najlepsi_plat(&self) -> Option<&Zamestnanec> {
self.zamestnanci.iter().max_by_key(|z| z.plat)
}
}
F2
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct Zamestnanec { meno: String, plat: u32 }
#[derive(Serialize, Deserialize, Default)]
struct Firma { zamestnanci: Vec<Zamestnanec> }
impl Firma {
fn nacitaj_zo_suboru(cesta: &std::path::PathBuf) -> Option<Firma> {
let raw = std::fs::read_to_string(cesta).ok()?;
serde_json::from_str(&raw).ok()
}
fn uloz_do_suboru(&self, cesta: &std::path::PathBuf) -> bool {
let Ok(json) = serde_json::to_string_pretty(&self) else {
return false;
};
std::fs::write(cesta, json).is_ok()
}
}
F3
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 {
eprintln!("Použi: pridaj <meno> <plat> | odstran <meno> | zoznam");
return;
}
match args[1].as_str() {
"pridaj" => {
if args.len() < 4 {
eprintln!("Použi: pridaj <meno> <plat>");
return;
}
let meno = &args[2];
let plat: u32 = match args[3].parse() {
Ok(p) => p,
Err(_) => { eprintln!("Neplatný plat"); return; }
};
println!("Pridávam: {} s platom {}", meno, plat);
}
"odstran" => {
if args.len() < 3 {
eprintln!("Použi: odstran <meno>");
return;
}
println!("Odstraňujem: {}", args[2]);
}
"zoznam" => {
println!("Zobrazujem zoznam...");
}
iny => eprintln!("Neznámy príkaz: {}", iny),
}
}
F4
// lib.rs
use serde::{Serialize, Deserialize};
use std::collections::HashMap;
#[derive(Serialize, Deserialize, Clone)]
pub struct Slovo {
pub original: String,
pub preklad: String,
pub kategoria: String,
}
#[derive(Serialize, Deserialize, Default)]
pub struct Slovnik {
pub slova: Vec<Slovo>,
}
impl Slovnik {
pub fn nacitaj_zo_suboru(cesta: &std::path::PathBuf) -> Option<Slovnik> {
let raw = std::fs::read_to_string(cesta).ok()?;
serde_json::from_str(&raw).ok()
}
pub fn uloz_do_suboru(&self, cesta: &std::path::PathBuf) -> bool {
let Ok(json) = serde_json::to_string_pretty(&self) else {
return false;
};
std::fs::write(cesta, json).is_ok()
}
pub fn pridaj_slovo(&mut self, slovo: Slovo) -> Result<(), ()> {
if self.slova.iter().any(|s| s.original == slovo.original) {
return Err(());
}
self.slova.push(slovo);
Ok(())
}
pub fn odstran_slovo(&mut self, original: &str) -> Result<Slovo, ()> {
let pos = self.slova.iter().position(|s| s.original == original).ok_or(())?;
Ok(self.slova.remove(pos))
}
pub fn preloz(&self, original: &str) -> Option<&str> {
self.slova.iter()
.find(|s| s.original == original)
.map(|s| s.preklad.as_str())
}
pub fn slova_v_kategorii(&self, kat: &str) -> Vec<&Slovo> {
self.slova.iter().filter(|s| s.kategoria == kat).collect()
}
pub fn pocet_v_kategoriach(&self) -> HashMap<String, usize> {
let mut mapa = HashMap::new();
for slovo in &self.slova {
*mapa.entry(slovo.kategoria.clone()).or_insert(0) += 1;
}
mapa
}
}
G1
use std::collections::HashMap;
fn pocitaj_znaky(text: &str) -> HashMap<char, usize> {
let mut mapa = HashMap::new();
for c in text.chars() {
*mapa.entry(c).or_insert(0) += 1;
}
mapa
}
G2
fn zdvojnasob_ak_existuje(x: Option<i32>) -> Option<i32> {
x.map(|n| n * 2)
}
G3
fn parsuj_option(maybe_text: Option<String>) -> Option<i32> {
maybe_text.and_then(|t| t.parse::<i32>().ok())
}
G4
fn nacitaj(cesta: &str) -> Result<String, String> {
std::fs::read_to_string(cesta).map_err(|e| e.to_string())
}
G5
Some(5)
None
None