28 KiB
Rust — Iterátory, Closures, Kombinácie (D–G)
Pre každú tému: vysvetlenie → príklady → úlohy na precvičenie → riešenia na konci.
Časť D: Iterátory
D.1 — Čo je iterátor
Iterátor je spôsob, ako prejsť cez kolekciu prvok po prvku. V Ruste existujú 3 spôsoby ako získať iterátor z kolekcie:
| Metóda | Dáva ti | Kedy použiť |
|---|---|---|
.iter() |
&T (nemeniteľná referencia) |
Chceš len čítať |
.iter_mut() |
&mut T (meniteľná referencia) |
Chceš meniť prvky na mieste |
.into_iter() |
T (vlastníctvo) |
Chceš konzumovať kolekciu |
let mut cisla = vec![1, 2, 3];
// iter() — len čítam, cisla existujú ďalej
for c in cisla.iter() {
println!("{}", c); // c je &i32
}
// iter_mut() — mením prvky
for c in cisla.iter_mut() {
*c += 10; // c je &mut i32, treba dereferencovať
}
// cisla je teraz [11, 12, 13]
// into_iter() — preberám vlastníctvo, cisla po tomto neexistujú
let cisla2 = vec![1, 2, 3];
for c in cisla2.into_iter() {
println!("{}", c); // c je i32
}
// cisla2 tu už nemôžeš použiť!
Pravidlo: Ak máš &self metódu (len čítaš), použi .iter(). Ak máš &mut self, použi .iter_mut().
D.2 — filter
Čo robí: Prejde cez iterátor a nechá len prvky, pre ktoré closure vráti true.
Typ: .filter() dáva referencie na referencie. Ak iteruješ cez &T, vo filtri dostaneš &&T.
let cisla = vec![1, 2, 3, 4, 5, 6];
// Nechaj len párne
let parne: Vec<&i32> = cisla.iter().filter(|c| *c % 2 == 0).collect();
// parne = [&2, &4, &6]
// Ak chceš Vec<i32> (nie referencie), použi .copied() alebo .cloned()
let parne: Vec<i32> = cisla.iter().filter(|c| *c % 2 == 0).copied().collect();
// parne = [2, 4, 6]
So štruktúrami:
struct Kniha { nazov: String, rok: u16 }
let knihy = vec![
Kniha { nazov: "A".into(), rok: 2020 },
Kniha { nazov: "B".into(), rok: 2015 },
];
// Knihy po roku 2018
let nove: Vec<&Kniha> = knihy.iter().filter(|k| k.rok > 2018).collect();
Na skúške: Každá metóda typu daj_knihy_podla_X je filter + collect.
D.3 — find
Čo robí: Vráti prvý prvok, pre ktorý closure vráti true. Vráti Option.
let cisla = vec![1, 2, 3, 4, 5];
let prve_parne: Option<&i32> = cisla.iter().find(|c| *c % 2 == 0);
// Some(&2)
let viac_ako_10: Option<&i32> = cisla.iter().find(|c| **c > 10);
// None
So štruktúrami:
let knihy = vec![
Kniha { nazov: "Hobbit".into(), rok: 1937 },
Kniha { nazov: "Duna".into(), rok: 1965 },
];
// Nájdi knihu podľa názvu
let najdena: Option<&Kniha> = knihy.iter().find(|k| k.nazov == "Duna");
Na skúške: Každá metóda typu daj_knihu_podla_isbn je find.
D.4 — position
Čo robí: Ako find, ale vráti index namiesto hodnoty. Vráti Option<usize>.
let mena = vec!["Anna".to_string(), "Boris".to_string(), "Cyril".to_string()];
let index: Option<usize> = mena.iter().position(|m| m == "Boris");
// Some(1)
Hlavné použitie — odstránenie z Vec:
fn odstran(zoznam: &mut Vec<String>, meno: &str) -> Result<String, ()> {
// 1. nájdi index
let pos = zoznam.iter().position(|s| s == meno).ok_or(())?;
// 2. odstráň na tom indexe (remove posunie zvyšné prvky)
Ok(zoznam.remove(pos))
}
Na skúške: Každá metóda odstran_knihu, odstran_kontakt atď. je position + remove.
D.5 — map
Čo robí: Transformuje každý prvok. [A, B, C] → [f(A), f(B), f(C)].
let cisla = vec![1, 2, 3];
// Zdvojnásob každé
let dvojnasobok: Vec<i32> = cisla.iter().map(|c| c * 2).collect();
// [2, 4, 6]
// Prevod na String
let texty: Vec<String> = cisla.iter().map(|c| c.to_string()).collect();
// ["1", "2", "3"]
// Vytiahni pole zo štruktúry
let nazvy: Vec<&String> = knihy.iter().map(|k| &k.nazov).collect();
D.6 — any a all
Čo robia: Vracajú bool.
let cisla = vec![1, -2, 3, -4, 5];
// any — existuje aspoň jeden, pre ktorý platí?
let ma_zaporne: bool = cisla.iter().any(|c| *c < 0);
// true
// all — platí pre VŠETKY?
let vsetky_kladne: bool = cisla.iter().all(|c| *c > 0);
// false
D.7 — sum a count
let cisla = vec![1, 2, 3, 4, 5];
// Súčet — musíš špecifikovať typ
let sucet: i32 = cisla.iter().sum();
// alebo
let sucet = cisla.iter().sum::<i32>();
// 15
// Počet prvkov spĺňajúcich podmienku
let pocet_parnych: usize = cisla.iter().filter(|c| *c % 2 == 0).count();
// 2
D.8 — max_by_key a min_by_key
Čo robí: Nájde maximum/minimum podľa kľúča. Vráti Option.
struct Zamestnanec { meno: String, plat: u32 }
let zamestnanci = vec![
Zamestnanec { meno: "Anna".into(), plat: 3000 },
Zamestnanec { meno: "Boris".into(), plat: 4500 },
Zamestnanec { meno: "Cyril".into(), plat: 2800 },
];
let najbohatsi: Option<&Zamestnanec> = zamestnanci.iter().max_by_key(|z| z.plat);
// Some(Boris)
D.9 — enumerate
Čo robí: Pridá index ku každému prvku. [A, B, C] → [(0, A), (1, B), (2, C)].
let mena = vec!["Anna", "Boris", "Cyril"];
for (i, meno) in mena.iter().enumerate() {
println!("{}. {}", i + 1, meno);
}
// 1. Anna
// 2. Boris
// 3. Cyril
D.10 — Reťazenie
Iterátory sa dajú reťaziť. Každá metóda vráti nový iterátor:
let zamestnanci = vec![/* ... */];
// Mená zamestnancov s platom nad 3000, zoradené
let bohati_mena: Vec<&String> = zamestnanci.iter()
.filter(|z| z.plat > 3000) // nechaj len bohatých
.map(|z| &z.meno) // vytiahni meno
.collect(); // zhromaždi do Vec
Kedy for, kedy iterátor:
- Jednoduchý prechod s výpisom →
for - Filtrovanie →
.filter().collect() - Transformácia →
.map().collect() - Hľadanie →
.find()alebo.position() - Podmienka →
.any()alebo.all() - Počítanie →
.filter().count() - Ak robíš kombináciu hore → reťaz iterátorov
Úlohy D
D-1. Máš Vec<i32> = [3, -1, 4, -1, 5, 9, -2, 6]. Napíš jedným výrazom (bez for):
- a) Počet kladných čísel
- b) Súčet záporných čísel
- c) Vec len kladných čísel (ako
Vec<i32>, nie referencie) - d) Či sú všetky čísla väčšie ako -5
- e) Či existuje číslo väčšie ako 8
D-2. Máš:
struct Student { meno: String, vek: u8, priemer: f32 }
let studenti: Vec<Student> = /* ... */;
Napíš (každé na jeden riadok):
- a) Mená študentov s priemerom pod 2.0 →
Vec<&String> - b) Najstarší študent →
Option<&Student> - c) Priemerný vek všetkých študentov →
Option<f64> - d) Existuje študent mladší ako 18? →
bool - e) Odstráň študenta podľa mena z
&mut Vec<Student>→Result<Student, ()>
D-3. Prepíš na iterátorový zápis bez for a bez mut premenných:
fn kladne_zdvojnasobene(cisla: &[i32]) -> Vec<i32> {
let mut vysledok = Vec::new();
for c in cisla {
if *c > 0 {
vysledok.push(c * 2);
}
}
vysledok
}
D-4. Čo je zle? Oprav:
fn zvys_vsetky(cisla: &mut Vec<i32>) {
for c in cisla.iter() {
*c += 1;
}
}
Časť E: Closures (uzávery)
E.1 — Čo je closure
Closure je anonymná funkcia, ktorá môže zachytiť premenné z okolitého prostredia.
// Normálna funkcia — nemá prístup k okoliu
fn pricti_5(x: i32) -> i32 {
x + 5
}
// Closure — zachytí premennú `zaklad` z okolia
let zaklad = 5;
let pricti = |x: i32| -> i32 { x + zaklad };
println!("{}", pricti(10)); // 15
E.2 — Syntax closure
Od najdlhšieho po najkratšie:
// Plný zápis (ako funkcia, ale s ||)
let f = |x: i32, y: i32| -> i32 { x + y };
// Bez typov (Rust si ich odvodí)
let f = |x, y| { x + y };
// Bez zátvoriek (ak je len jeden výraz)
let f = |x, y| x + y;
// Bez parametrov
let f = || println!("ahoj");
// S jedným parametrom
let f = |x| x * 2;
Pravidlo: V .filter(), .map(), .find() atď. vždy píš najkratší zápis.
E.3 — Closures v iterátoroch
Toto sú najčastejšie situácie:
let cisla = vec![1, 2, 3, 4, 5];
// filter — closure berie &&T (referenciu na referenciu)
cisla.iter().filter(|c| **c > 3) // c je &&i32, treba **
cisla.iter().filter(|&c| *c > 3) // destructuring v parametri
cisla.iter().filter(|&&c| c > 3) // plný destructuring
// map — closure berie &T
cisla.iter().map(|c| c * 2) // c je &i32, Rust automaticky dereferencuje pri *
// find — rovnako ako filter
cisla.iter().find(|c| **c == 3)
// any/all — rovnako ako filter
cisla.iter().any(|c| *c > 3) // tu je len &T, nie &&T!
Prečo filter má &&T? Lebo .iter() dáva &T, a filter ešte obalí ďalšou referenciou. Ale Rust je v praxi dosť chytrý — väčšinou stačí |c| c > &3 alebo |c| *c > 3.
E.4 — move closure
Normálne closure zachytáva referencie. move prenesie vlastníctvo do closure:
// BEZ move — closure zachytí &meno
let meno = "Anna".to_string();
let pozdrav = || println!("Ahoj {}", meno);
pozdrav();
println!("{}", meno); // OK, meno stále existuje
// S move — closure prevezme vlastníctvo mena
let meno = "Anna".to_string();
let pozdrav = move || println!("Ahoj {}", meno);
pozdrav();
// println!("{}", meno); // CHYBA! meno bolo presunuté do closure
Kedy treba move: Keď closure musí prežiť pôvodnú premennú (napr. vrátenie closure z funkcie).
fn vytvor_pozdrav(meno: String) -> impl Fn() {
// BEZ move: CHYBA — meno zomrie na konci funkcie, ale closure ho referencuje
// S move: OK — closure vlastní meno
move || println!("Ahoj {}", meno)
}
E.5 — Closure zachytávajúci &mut
Ak closure modifikuje premennú, zachytí ju ako &mut:
let mut pocitadlo = 0;
let mut zvys = || { pocitadlo += 1; };
zvys();
zvys();
zvys();
println!("{}", pocitadlo); // 3
Dôležité: Kým existuje &mut closure, nemôžeš čítať premennú inak:
let mut cisla = vec![1, 2, 3];
let mut pridaj = |x| cisla.push(x);
pridaj(4);
// println!("{:?}", cisla); // CHYBA TU! pridaj stále drží &mut cisla
pridaj(5);
drop(pridaj); // explicitne uvoľni closure
println!("{:?}", cisla); // OK teraz — [1, 2, 3, 4, 5]
Alebo jednoducho — posledné použitie closure pred prvým iným použitím:
let mut cisla = vec![1, 2, 3];
let mut pridaj = |x| cisla.push(x);
pridaj(4);
pridaj(5); // posledné volanie
println!("{:?}", cisla); // OK — Rust vie, že pridaj sa už nepoužije
Úlohy E
E-1. Napíš 3 verzie tej istej operácie (vyfiltruj čísla > 10 z Vec):
- a) S pomenovanou funkciou (nie closure)
- b) S plným zápisom closure (aj typy)
- c) S najkratším zápisom
E-2. Čo je zle? Oprav:
fn vrat_nasobicku(faktor: i32) -> impl Fn(i32) -> i32 {
|x| x * faktor
}
E-3. Napíš funkciu aplikuj_na_vsetky(cisla: &[i32], f: impl Fn(i32) -> i32) -> Vec<i32> ktorá aplikuje funkciu f na každý prvok.
Potom ju zavolaj s:
- a) closure, ktorý zdvojnásobí
- b) closure, ktorý pripočíta 100
- c) pomenovanou funkciou, ktorá vráti absolútnu hodnotu
E-4. Máš Vec<String>. Napíš closure, ktorý:
- Zachytí
&mut Vec<String> - Pridá reťazec do vektora
- Zavolaj ho 3x, potom vypíš výsledok
Časť F: HashMap
F.1 — Základy
use std::collections::HashMap;
let mut mapa: HashMap<String, i32> = HashMap::new();
// Vloženie
mapa.insert("Anna".to_string(), 25);
mapa.insert("Boris".to_string(), 30);
// Čítanie — vráti Option<&V>
let vek: Option<&i32> = mapa.get("Anna"); // Some(&25)
let vek: Option<&i32> = mapa.get("Cyril"); // None
// Kontrola existencie
let existuje: bool = mapa.contains_key("Anna"); // true
// Iterácia
for (meno, vek) in &mapa {
println!("{}: {}", meno, vek);
}
F.2 — Entry API (dôležité na skúške!)
entry() je spôsob, ako pracovať s kľúčom, ktorý môže alebo nemusí existovať:
use std::collections::HashMap;
let mut pocty: HashMap<String, usize> = HashMap::new();
let slova = vec!["ahoj", "svet", "ahoj", "rust", "svet", "ahoj"];
// Počítaj výskyty
for slovo in &slova {
// entry() vráti Entry — buď Occupied alebo Vacant
// or_insert(0) — ak kľúč neexistuje, vlož 0
// vráti &mut hodnotu
*pocty.entry(slovo.to_string()).or_insert(0) += 1;
}
// pocty = {"ahoj": 3, "svet": 2, "rust": 1}
Rozklad:
*pocty.entry(slovo.to_string()).or_insert(0) += 1;
// ^^^^^ nájdi alebo vytvor kľúč
// ^^^^^^^^^^^^ ak neexistuje, vlož 0
// ^^^^ vráti &mut usize
// ^^^ += 1 dereferencuj a zvýš
Na skúške: Metódy ako vypis_vydavatelstva_a_pocet_knih alebo pocet_v_kategoriach vždy používajú tento vzor.
F.3 — HashMap zo štruktúr
Typický vzor na skúške — zoskup podľa poľa a spočítaj:
struct Kniha { nazov: String, vydavatelstvo: String }
fn pocty_podla_vydavatelstva(knihy: &[Kniha]) -> HashMap<String, usize> {
let mut mapa = HashMap::new();
for kniha in knihy {
*mapa.entry(kniha.vydavatelstvo.clone()).or_insert(0) += 1;
}
mapa
}
// Výpis:
fn vypis_statistiky(knihy: &[Kniha]) {
let pocty = pocty_podla_vydavatelstva(knihy);
for (vydavatelstvo, pocet) in &pocty {
println!("{}: {}", vydavatelstvo, pocet);
}
}
F.4 — or_insert_with a or_default
// or_insert_with — lazy verzia, closure sa zavolá len ak kľúč neexistuje
mapa.entry(kluc).or_insert_with(|| Vec::new());
// or_default — použije Default trait
// Pre Vec → prázdny vektor, pre usize → 0, pre String → ""
mapa.entry(kluc).or_default();
// Príklad: zoskupenie kníh podľa žánru
let mut podla_zanru: HashMap<String, Vec<&Kniha>> = HashMap::new();
for kniha in &knihy {
podla_zanru.entry(kniha.zaner.clone()).or_default().push(kniha);
}
Úlohy F
F-1. Napíš funkciu pocitaj_znaky(text: &str) -> HashMap<char, usize> — počet výskytov každého znaku.
F-2. Máš Vec<Student> kde Student má meno, trieda: String, priemer: f32. Napíš:
- a)
pocet_v_triedach(studenti: &[Student]) -> HashMap<String, usize>— koľko študentov v každej triede - b)
najlepsi_v_triede(studenti: &[Student]) -> HashMap<String, &Student>— študent s najnižším priemerom v každej triede
F-3. Napíš funkciu invertuj_mapu(mapa: &HashMap<String, String>) -> HashMap<String, String> — otočí kľúče a hodnoty.
Časť G: Option/Result kombinátory
G.1 — .map() na Option
Čo robí: Ak je Some(x), aplikuje funkciu na x. Ak None, zostane None.
let a: Option<i32> = Some(5);
let b: Option<i32> = None;
let a2: Option<i32> = a.map(|x| x * 2); // Some(10)
let b2: Option<i32> = b.map(|x| x * 2); // None
// Praktické — vytiahni pole zo Option<&Struct>
let kniha: Option<&Kniha> = najdi_knihu("Duna");
let nazov: Option<&String> = kniha.map(|k| &k.nazov);
// Prevod typu
let cislo: Option<i32> = Some(42);
let text: Option<String> = cislo.map(|c| c.to_string()); // Some("42")
Nahrádza:
// Toto:
match opt {
Some(x) => Some(f(x)),
None => None,
}
// Je to isté ako:
opt.map(f)
G.2 — .and_then() (flatmap)
Čo robí: Ako .map(), ale closure vracia Option. Zabraňuje Option<Option<T>>.
// map by vytvorilo Option<Option<i32>>:
let text: Option<String> = Some("42".to_string());
let zle: Option<Option<i32>> = text.map(|t| t.parse::<i32>().ok());
// and_then "sploští":
let text: Option<String> = Some("42".to_string());
let dobre: Option<i32> = text.and_then(|t| t.parse::<i32>().ok());
// Some(42)
let text2: Option<String> = Some("abc".to_string());
let dobre2: Option<i32> = text2.and_then(|t| t.parse::<i32>().ok());
// None (parse zlyhalo)
Pravidlo: Ak tvoja closure vracia Option → použi .and_then(). Ak vracia holú hodnotu → použi .map().
G.3 — .map_err()
Čo robí: Transformuje chybu v Result, hodnotu necháva.
// read_to_string vracia Result<String, io::Error>
// Ty chceš Result<String, String>
fn nacitaj(cesta: &str) -> Result<String, String> {
std::fs::read_to_string(cesta)
.map_err(|e| format!("Chyba čítania: {}", e))
}
G.4 — .flatten()
Čo robí: Option<Option<T>> → Option<T>, Result<Result<T, E>, E> → Result<T, E>.
let a: Option<Option<i32>> = Some(Some(5));
let b: Option<Option<i32>> = Some(None);
let c: Option<Option<i32>> = None;
a.flatten() // Some(5)
b.flatten() // None
c.flatten() // None
G.5 — .unwrap_or(), .unwrap_or_default(), .unwrap_or_else()
let a: Option<i32> = None;
a.unwrap_or(0) // 0 — vždy vyhodnotí fallback
a.unwrap_or_default() // 0 — použije Default trait (i32 default = 0)
a.unwrap_or_else(|| { // lazy — closure sa zavolá len ak None
println!("Počítam fallback...");
vypocitaj_nieco()
})
Kedy ktorý:
.unwrap_or(hodnota)— fallback je jednoduchá konštanta.unwrap_or_default()— chceš default pre daný typ (0, "", Vec::new(), false).unwrap_or_else(|| ...)— fallback je drahý výpočet
Úlohy G
G-1. Prepíš bez match:
fn zdvojnasob_option(x: Option<i32>) -> Option<i32> {
match x {
Some(n) => Some(n * 2),
None => None,
}
}
G-2. Prepíš bez match:
fn parsuj_prvy_znak(text: Option<String>) -> Option<char> {
match text {
Some(t) => {
match t.chars().next() {
Some(c) => Some(c),
None => None,
}
}
None => None,
}
}
G-3. Napíš funkciu bezpecne_delenie(a: f64, b: f64) -> Option<f64> — vráti None ak b == 0.0. Bez if, použi .filter() alebo podmienku v .and_then().
Potom napíš: delenie_s_fallbackom(a: f64, b: f64) -> f64 — ak sa nedá deliť, vráti 0.0. Použi predošlú funkciu + .unwrap_or().
G-4. Máš:
fn najdi_pouzivatela(id: u32) -> Option<String> { /* ... */ }
fn najdi_email(meno: &str) -> Option<String> { /* ... */ }
Napíš funkciu email_pre_id(id: u32) -> Option<String> — nájdi použivateľa podľa id, potom jeho email. Jeden riadok s .and_then().
G-5. Čo vypíše? Najprv tipni, potom over:
let a: Option<i32> = Some(5);
println!("{:?}", a.map(|x| x > 3));
println!("{:?}", a.filter(|x| *x > 3));
println!("{:?}", a.filter(|x| *x > 10));
println!("{:?}", a.map(|x| x.to_string()));
println!("{:?}", a.and_then(|x| if x > 3 { Some(x * 2) } else { None }));
Časť H: Kombinované cvičenia (simulácia skúšky)
H-1. Kompletný CRUD
Implementuj bez pozerania na čokoľvek:
use serde::{Serialize, Deserialize};
use std::collections::HashMap;
#[derive(Serialize, Deserialize, Clone)]
struct Produkt {
nazov: String,
cena: f64,
kategoria: String,
na_sklade: bool,
}
#[derive(Serialize, Deserialize, Default)]
struct Obchod {
produkty: Vec<Produkt>,
}
impl Obchod {
fn nacitaj_zo_suboru(cesta: &std::path::PathBuf) -> Option<Obchod> { todo!() }
fn uloz_do_suboru(&self, cesta: &std::path::PathBuf) -> bool { todo!() }
fn pridaj(&mut self, produkt: Produkt) -> Result<(), ()> { todo!() }
// duplicita podľa nazvu
fn odstran(&mut self, nazov: &str) -> Result<Produkt, ()> { todo!() }
fn najdi(&self, nazov: &str) -> Option<&Produkt> { todo!() }
fn na_sklade(&self) -> Vec<&Produkt> { todo!() }
fn v_kategorii(&self, kat: &str) -> Vec<&Produkt> { todo!() }
fn najdrahsi(&self) -> Option<&Produkt> { todo!() }
fn priemerny_cena_v_kategorii(&self, kat: &str) -> Option<f64> { todo!() }
fn pocty_kategorii(&self) -> HashMap<String, usize> { todo!() }
}
H-2. Display enum + struct
use std::fmt;
enum Velkost { Mala, Stredna, Velka }
// Implementuj Display: "S", "M", "L"
struct Tricko { farba: String, velkost: Velkost, cena: f64 }
// Implementuj Display: "Tričko [M] červené - 19.99€"
RIEŠENIA
D-1
let cisla = vec![3, -1, 4, -1, 5, 9, -2, 6];
// a)
let pocet_kladnych: usize = cisla.iter().filter(|c| **c > 0).count();
// 5
// b)
let sucet_zapornych: i32 = cisla.iter().filter(|c| **c < 0).sum();
// -4
// c)
let kladne: Vec<i32> = cisla.iter().filter(|c| **c > 0).copied().collect();
// [3, 4, 5, 9, 6]
// d)
let vsetky_nad_minus5: bool = cisla.iter().all(|c| *c > -5);
// true
// e)
let existuje_nad_8: bool = cisla.iter().any(|c| *c > 8);
// true
D-2
struct Student { meno: String, vek: u8, priemer: f32 }
// a)
let mena: Vec<&String> = studenti.iter().filter(|s| s.priemer < 2.0).map(|s| &s.meno).collect();
// b)
let najstarsi: Option<&Student> = studenti.iter().max_by_key(|s| s.vek);
// c)
fn priemerny_vek(studenti: &[Student]) -> Option<f64> {
if studenti.is_empty() { return None; }
let sucet: u32 = studenti.iter().map(|s| s.vek as u32).sum();
Some(sucet as f64 / studenti.len() as f64)
}
// d)
let ma_mladsiho: bool = studenti.iter().any(|s| s.vek < 18);
// e)
fn odstran_studenta(studenti: &mut Vec<Student>, meno: &str) -> Result<Student, ()> {
let pos = studenti.iter().position(|s| s.meno == meno).ok_or(())?;
Ok(studenti.remove(pos))
}
D-3
fn kladne_zdvojnasobene(cisla: &[i32]) -> Vec<i32> {
cisla.iter().filter(|c| **c > 0).map(|c| c * 2).collect()
}
D-4
iter() dáva &i32 (nemeniteľná referencia). Nemôžeš cez ňu meniť. Treba iter_mut():
fn zvys_vsetky(cisla: &mut Vec<i32>) {
for c in cisla.iter_mut() {
*c += 1;
}
}
E-1
let cisla = vec![1, 5, 12, 3, 15, 8];
// a) pomenovaná funkcia
fn je_viac_ako_10(c: &&i32) -> bool { **c > 10 }
let vysledok: Vec<&i32> = cisla.iter().filter(je_viac_ako_10).collect();
// b) plný zápis
let vysledok: Vec<&i32> = cisla.iter().filter(|c: &&i32| -> bool { **c > 10 }).collect();
// c) najkratší
let vysledok: Vec<&i32> = cisla.iter().filter(|c| **c > 10).collect();
E-2
Treba move — faktor je lokálna premenná, closure ho musí vlastniť:
fn vrat_nasobicku(faktor: i32) -> impl Fn(i32) -> i32 {
move |x| x * faktor
}
E-3
fn aplikuj_na_vsetky(cisla: &[i32], f: impl Fn(i32) -> i32) -> Vec<i32> {
cisla.iter().map(|c| f(*c)).collect()
}
fn main() {
let cisla = vec![1, -2, 3, -4, 5];
// a)
let a = aplikuj_na_vsetky(&cisla, |x| x * 2);
// b)
let b = aplikuj_na_vsetky(&cisla, |x| x + 100);
// c)
fn absolutna(x: i32) -> i32 { x.abs() }
let c = aplikuj_na_vsetky(&cisla, absolutna);
}
E-4
fn main() {
let mut zoznam = Vec::new();
let mut pridaj = |s: &str| zoznam.push(s.to_string());
pridaj("ahoj");
pridaj("svet");
pridaj("rust");
println!("{:?}", zoznam); // ["ahoj", "svet", "rust"]
}
F-1
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
}
F-2
struct Student { meno: String, trieda: String, priemer: f32 }
// a)
fn pocet_v_triedach(studenti: &[Student]) -> HashMap<String, usize> {
let mut mapa = HashMap::new();
for s in studenti {
*mapa.entry(s.trieda.clone()).or_insert(0) += 1;
}
mapa
}
// b)
fn najlepsi_v_triede<'a>(studenti: &'a [Student]) -> HashMap<String, &'a Student> {
let mut mapa: HashMap<String, &Student> = HashMap::new();
for s in studenti {
mapa.entry(s.trieda.clone())
.and_modify(|najlepsi| {
if s.priemer < najlepsi.priemer {
*najlepsi = s;
}
})
.or_insert(s);
}
mapa
}
F-3
fn invertuj_mapu(mapa: &HashMap<String, String>) -> HashMap<String, String> {
let mut nova = HashMap::new();
for (k, v) in mapa {
nova.insert(v.clone(), k.clone());
}
nova
}
G-1
fn zdvojnasob_option(x: Option<i32>) -> Option<i32> {
x.map(|n| n * 2)
}
G-2
fn parsuj_prvy_znak(text: Option<String>) -> Option<char> {
text.and_then(|t| t.chars().next())
}
G-3
fn bezpecne_delenie(a: f64, b: f64) -> Option<f64> {
Some(b).filter(|b| *b != 0.0).map(|b| a / b)
}
fn delenie_s_fallbackom(a: f64, b: f64) -> f64 {
bezpecne_delenie(a, b).unwrap_or(0.0)
}
G-4
fn email_pre_id(id: u32) -> Option<String> {
najdi_pouzivatela(id).and_then(|meno| najdi_email(&meno))
}
G-5
Some(true) // map: 5 > 3 = true, zabalí do Some
Some(5) // filter: 5 > 3 je true, nechá Some(5)
None // filter: 5 > 10 je false, vráti None
Some("5") // map: 5.to_string() = "5"
Some(10) // and_then: 5 > 3, tak Some(5 * 2) = Some(10)
H-1
use serde::{Serialize, Deserialize};
use std::collections::HashMap;
#[derive(Serialize, Deserialize, Clone)]
struct Produkt {
nazov: String,
cena: f64,
kategoria: String,
na_sklade: bool,
}
#[derive(Serialize, Deserialize, Default)]
struct Obchod {
produkty: Vec<Produkt>,
}
impl Obchod {
fn nacitaj_zo_suboru(cesta: &std::path::PathBuf) -> Option<Obchod> {
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()
}
fn pridaj(&mut self, produkt: Produkt) -> Result<(), ()> {
if self.produkty.iter().any(|p| p.nazov == produkt.nazov) {
return Err(());
}
self.produkty.push(produkt);
Ok(())
}
fn odstran(&mut self, nazov: &str) -> Result<Produkt, ()> {
let pos = self.produkty.iter().position(|p| p.nazov == nazov).ok_or(())?;
Ok(self.produkty.remove(pos))
}
fn najdi(&self, nazov: &str) -> Option<&Produkt> {
self.produkty.iter().find(|p| p.nazov == nazov)
}
fn na_sklade(&self) -> Vec<&Produkt> {
self.produkty.iter().filter(|p| p.na_sklade).collect()
}
fn v_kategorii(&self, kat: &str) -> Vec<&Produkt> {
self.produkty.iter().filter(|p| p.kategoria == kat).collect()
}
fn najdrahsi(&self) -> Option<&Produkt> {
self.produkty.iter().max_by(|a, b| a.cena.partial_cmp(&b.cena).unwrap())
}
fn priemerna_cena_v_kategorii(&self, kat: &str) -> Option<f64> {
let v_kat: Vec<&Produkt> = self.v_kategorii(kat);
if v_kat.is_empty() { return None; }
let sucet: f64 = v_kat.iter().map(|p| p.cena).sum();
Some(sucet / v_kat.len() as f64)
}
fn pocty_kategorii(&self) -> HashMap<String, usize> {
let mut mapa = HashMap::new();
for p in &self.produkty {
*mapa.entry(p.kategoria.clone()).or_insert(0) += 1;
}
mapa
}
}
H-2
use std::fmt;
enum Velkost { Mala, Stredna, Velka }
impl fmt::Display for Velkost {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Velkost::Mala => write!(f, "S"),
Velkost::Stredna => write!(f, "M"),
Velkost::Velka => write!(f, "L"),
}
}
}
struct Tricko { farba: String, velkost: Velkost, cena: f64 }
impl fmt::Display for Tricko {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Tričko [{}] {} - {:.2}€", self.velkost, self.farba, self.cena)
}
}