821 lines
21 KiB
Markdown
821 lines
21 KiB
Markdown
# I/O v Ruste — krok za krokom
|
|
|
|
Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej.
|
|
|
|
---
|
|
|
|
## Kapitola 1: println! a print! — výstup
|
|
|
|
```rust
|
|
// println! — vypíše + nový riadok
|
|
println!("Ahoj svet");
|
|
// Ahoj svet\n
|
|
|
|
// print! — vypíše BEZ nového riadku
|
|
print!("Zadaj meno: ");
|
|
// Zadaj meno: _ (kurzor zostane na tom istom riadku)
|
|
```
|
|
|
|
### Formátovanie
|
|
|
|
```rust
|
|
let meno = "Anna";
|
|
let vek = 25;
|
|
let priemer = 1.847;
|
|
|
|
// Základné
|
|
println!("{} má {} rokov", meno, vek);
|
|
// Anna má 25 rokov
|
|
|
|
// Zaokrúhlenie na 2 desatinné
|
|
println!("Priemer: {:.2}", priemer);
|
|
// Priemer: 1.85
|
|
|
|
// Zarovnanie
|
|
println!("{:<10} | {:>5}", "Meno", "Vek");
|
|
println!("{:<10} | {:>5}", "Anna", 25);
|
|
println!("{:<10} | {:>5}", "Boris", 30);
|
|
// Meno | Vek
|
|
// Anna | 25
|
|
// Boris | 30
|
|
|
|
// Padding znakom
|
|
println!("{:-^20}", "MENU");
|
|
// --------MENU--------
|
|
|
|
// Debug výpis
|
|
let vektor = vec![1, 2, 3];
|
|
println!("{:?}", vektor); // [1, 2, 3]
|
|
println!("{:#?}", vektor); // pekný formát na viac riadkov
|
|
```
|
|
|
|
### eprintln! — chybový výstup
|
|
|
|
```rust
|
|
// eprintln! ide na stderr (nie stdout)
|
|
eprintln!("Chyba: súbor neexistuje");
|
|
|
|
// V praxi: normálne správy cez println!, chyby cez eprintln!
|
|
```
|
|
|
|
### Úlohy 1
|
|
|
|
**1a.** Vypíš tabuľku 3 produktov s názvom (zarovnaný vľavo na 15), cenou (zarovnaná vpravo na 8, 2 desatinné) a počtom (zarovnaný vpravo na 5):
|
|
```
|
|
Chlieb | 1.20 | 50
|
|
Mlieko | 0.89 | 120
|
|
Maslo | 2.49 | 30
|
|
```
|
|
|
|
**1b.** Máš Vec<(String, f64)> — mená a priemery. Vypíš ich zoradené podľa priemeru, formátované na 1 desatinné miesto.
|
|
|
|
---
|
|
|
|
## Kapitola 2: stdin — čítanie vstupu
|
|
|
|
```rust
|
|
use std::io;
|
|
|
|
fn main() {
|
|
// Základné čítanie riadku
|
|
let mut vstup = String::new();
|
|
io::stdin().read_line(&mut vstup).unwrap();
|
|
// vstup teraz obsahuje text + '\n' na konci!
|
|
|
|
let vstup = vstup.trim(); // odstráň \n a medzery
|
|
println!("Napísal si: {}", vstup);
|
|
}
|
|
```
|
|
|
|
### Krok po kroku
|
|
|
|
```rust
|
|
// 1. Vytvor prázdny String (buffer)
|
|
let mut vstup = String::new();
|
|
|
|
// 2. Prečítaj riadok do bufferu
|
|
// read_line PRIDÁVA do stringu (neprepisuje!)
|
|
// Vracia Result<usize, io::Error> (počet prečítaných bajtov)
|
|
io::stdin().read_line(&mut vstup).unwrap();
|
|
|
|
// 3. VŽDY trimni — read_line pridáva '\n'
|
|
let vstup = vstup.trim();
|
|
```
|
|
|
|
### Čítanie čísla
|
|
|
|
```rust
|
|
fn nacitaj_cislo() -> Option<i32> {
|
|
let mut vstup = String::new();
|
|
io::stdin().read_line(&mut vstup).ok()?;
|
|
vstup.trim().parse().ok()
|
|
}
|
|
|
|
// Použitie:
|
|
match nacitaj_cislo() {
|
|
Some(n) => println!("Číslo: {}", n),
|
|
None => println!("To nie je číslo"),
|
|
}
|
|
```
|
|
|
|
### Čítanie jedného znaku
|
|
|
|
```rust
|
|
fn nacitaj_znak() -> Option<char> {
|
|
let mut vstup = String::new();
|
|
std::io::stdin().read_line(&mut vstup).ok()?;
|
|
vstup.trim().chars().next()
|
|
}
|
|
|
|
// Použitie:
|
|
match nacitaj_znak() {
|
|
Some(c) => println!("Znak: {}", c),
|
|
None => println!("Prázdny vstup"),
|
|
}
|
|
```
|
|
|
|
### Pozor: read_line pridáva, neprepisuje!
|
|
|
|
```rust
|
|
let mut vstup = String::new();
|
|
io::stdin().read_line(&mut vstup).unwrap(); // "ahoj\n"
|
|
io::stdin().read_line(&mut vstup).unwrap(); // "ahoj\nsvet\n" — PRIDALO!
|
|
|
|
// Ak chceš čítať viackrát, buď trimuj alebo vytvor nový String
|
|
let mut vstup = String::new();
|
|
io::stdin().read_line(&mut vstup).unwrap();
|
|
let prvy = vstup.trim().to_string();
|
|
|
|
vstup.clear(); // VYMAŽ pred ďalším čítaním!
|
|
io::stdin().read_line(&mut vstup).unwrap();
|
|
let druhy = vstup.trim().to_string();
|
|
```
|
|
|
|
### Úlohy 2
|
|
|
|
**2a.** Napíš program, ktorý prečíta meno a vek od používateľa a vypíše `"Ahoj Anna, máš 25 rokov"`. Vek parsuj na u32.
|
|
|
|
**2b.** Napíš funkciu `nacitaj_riadok() -> Option<String>` — prečíta riadok, trimne, vráti None ak je prázdny.
|
|
|
|
**2c.** Napíš funkciu `nacitaj_float() -> Option<f64>` — prečíta riadok, trimne, parsne na f64.
|
|
|
|
**2d.** Napíš program, ktorý opakovane číta čísla od používateľa kým nezadá "koniec". Na konci vypíše súčet.
|
|
|
|
---
|
|
|
|
## Kapitola 3: Game loop — opakované čítanie
|
|
|
|
Na skúške je typický vzor: čítaj vstup v cykle, reaguj, skonči keď podmienka.
|
|
|
|
### Základný game loop
|
|
|
|
```rust
|
|
use std::io;
|
|
|
|
fn main() {
|
|
loop {
|
|
print!("Zadaj príkaz: ");
|
|
|
|
let mut vstup = String::new();
|
|
io::stdin().read_line(&mut vstup).unwrap();
|
|
let vstup = vstup.trim();
|
|
|
|
match vstup {
|
|
"koniec" => {
|
|
println!("Zbohom!");
|
|
break;
|
|
}
|
|
"pomoc" => {
|
|
println!("Príkazy: koniec, pomoc");
|
|
}
|
|
_ => {
|
|
println!("Neznámy príkaz: {}", vstup);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Game loop s parsovaním
|
|
|
|
```rust
|
|
loop {
|
|
print!("> ");
|
|
let mut vstup = String::new();
|
|
io::stdin().read_line(&mut vstup).unwrap();
|
|
let casti: Vec<&str> = vstup.trim().split_whitespace().collect();
|
|
|
|
if casti.is_empty() { continue; }
|
|
|
|
match casti[0] {
|
|
"pridaj" => {
|
|
if casti.len() < 2 {
|
|
println!("Použitie: pridaj <meno>");
|
|
continue;
|
|
}
|
|
let meno = casti[1];
|
|
println!("Pridávam: {}", meno);
|
|
}
|
|
"vypis" => {
|
|
println!("Výpis...");
|
|
}
|
|
"koniec" => break,
|
|
_ => println!("Neznámy príkaz"),
|
|
}
|
|
}
|
|
```
|
|
|
|
### Úlohy 3
|
|
|
|
**3a.** Napíš jednoduchý kalkulátor. Používateľ zadáva `"5 + 3"`, `"10 - 4"`, `"koniec"`. Program počíta a vypisuje výsledok.
|
|
|
|
**3b.** Napíš program, kde používateľ opakovane háda číslo 1-100. Program povie "viac", "menej" alebo "správne". Použi `rand::Rng`.
|
|
|
|
---
|
|
|
|
## Kapitola 4: Súborové I/O — čítanie
|
|
|
|
```rust
|
|
use std::fs;
|
|
|
|
// Prečítaj celý súbor naraz do String
|
|
let obsah: Result<String, std::io::Error> = fs::read_to_string("data.txt");
|
|
|
|
match obsah {
|
|
Ok(text) => println!("Obsah: {}", text),
|
|
Err(e) => println!("Chyba: {}", e),
|
|
}
|
|
|
|
// Skrátene s .ok()? v funkcii
|
|
fn nacitaj(cesta: &str) -> Option<String> {
|
|
fs::read_to_string(cesta).ok()
|
|
}
|
|
```
|
|
|
|
### Čítanie po riadkoch
|
|
|
|
```rust
|
|
use std::fs;
|
|
|
|
fn spracuj_subor(cesta: &str) {
|
|
let obsah = fs::read_to_string(cesta).unwrap();
|
|
for (i, riadok) in obsah.lines().enumerate() {
|
|
println!("{}: {}", i + 1, riadok);
|
|
}
|
|
}
|
|
```
|
|
|
|
### BufReader — efektívne čítanie po riadkoch
|
|
|
|
Pre veľké súbory je lepší BufReader — nečíta celý súbor do pamäte:
|
|
|
|
```rust
|
|
use std::fs::File;
|
|
use std::io::{BufRead, BufReader};
|
|
|
|
fn citaj_po_riadkoch(cesta: &str) -> Result<(), std::io::Error> {
|
|
let subor = File::open(cesta)?;
|
|
let citac = BufReader::new(subor);
|
|
|
|
for riadok in citac.lines() {
|
|
let riadok = riadok?; // každý riadok je Result
|
|
println!("{}", riadok);
|
|
}
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
### Čítanie bajtov
|
|
|
|
```rust
|
|
// Celý súbor ako bajty
|
|
let bajty: Vec<u8> = fs::read("obrazok.png").unwrap();
|
|
println!("Veľkosť: {} bajtov", bajty.len());
|
|
```
|
|
|
|
### Úlohy 4
|
|
|
|
**4a.** Napíš funkciu `pocet_riadkov(cesta: &str) -> Option<usize>` — prečíta súbor a vráti počet riadkov.
|
|
|
|
**4b.** Napíš funkciu `najdi_v_subore(cesta: &str, hladany: &str) -> Vec<String>` — vráti riadky, ktoré obsahujú hľadaný text.
|
|
|
|
**4c.** Napíš funkciu `nacitaj_cisla(cesta: &str) -> Option<Vec<i32>>` — súbor obsahuje jedno číslo na riadok. Prečítaj, parsuj, vráť Vec.
|
|
|
|
---
|
|
|
|
## Kapitola 5: Súborové I/O — zápis
|
|
|
|
```rust
|
|
use std::fs;
|
|
|
|
// Zapíš celý String do súboru (prepíše ak existuje!)
|
|
fs::write("data.txt", "Ahoj svet\nDruhý riadok\n").unwrap();
|
|
|
|
// Bezpečná verzia v funkcii
|
|
fn uloz(cesta: &str, obsah: &str) -> bool {
|
|
fs::write(cesta, obsah).is_ok()
|
|
}
|
|
```
|
|
|
|
### Pridávanie na koniec (append)
|
|
|
|
```rust
|
|
use std::fs::OpenOptions;
|
|
use std::io::Write;
|
|
|
|
fn pripis(cesta: &str, text: &str) -> bool {
|
|
let vysledok = OpenOptions::new()
|
|
.create(true) // vytvor ak neexistuje
|
|
.append(true) // pridávaj na koniec
|
|
.open(cesta);
|
|
|
|
match vysledok {
|
|
Ok(mut subor) => {
|
|
writeln!(subor, "{}", text).is_ok()
|
|
}
|
|
Err(_) => false,
|
|
}
|
|
}
|
|
```
|
|
|
|
### BufWriter — efektívny zápis
|
|
|
|
```rust
|
|
use std::fs::File;
|
|
use std::io::{BufWriter, Write};
|
|
|
|
fn zapis_vela_riadkov(cesta: &str, riadky: &[String]) -> bool {
|
|
let Ok(subor) = File::create(cesta) else {
|
|
return false;
|
|
};
|
|
let mut writer = BufWriter::new(subor);
|
|
for riadok in riadky {
|
|
if writeln!(writer, "{}", riadok).is_err() {
|
|
return false;
|
|
}
|
|
}
|
|
true
|
|
}
|
|
```
|
|
|
|
### Úlohy 5
|
|
|
|
**5a.** Napíš funkciu `uloz_riadky(cesta: &str, riadky: &[String]) -> bool` — ulož vektor riadkov do súboru, každý na nový riadok.
|
|
|
|
**5b.** Napíš funkciu `pridaj_riadok(cesta: &str, riadok: &str) -> bool` — pridaj riadok na koniec súboru.
|
|
|
|
**5c.** Napíš program, ktorý číta mená od používateľa a ukladá ich do `"mena.txt"`. Keď zadá "koniec", skončí.
|
|
|
|
---
|
|
|
|
## Kapitola 6: PathBuf a Path
|
|
|
|
`PathBuf` je ako `String` pre cesty k súborom. `Path` je ako `&str`.
|
|
|
|
```rust
|
|
use std::path::{PathBuf, Path};
|
|
|
|
// Vytváranie PathBuf
|
|
let cesta = PathBuf::from("data/subory/kniznica.json");
|
|
let cesta: PathBuf = "data.json".into();
|
|
|
|
// Z &str
|
|
let cesta = Path::new("data.json");
|
|
|
|
// Konverzia
|
|
let pathbuf = PathBuf::from("data.json");
|
|
let path: &Path = &pathbuf; // PathBuf → &Path
|
|
let path: &Path = pathbuf.as_path(); // to isté
|
|
|
|
// Užitočné metódy
|
|
let cesta = PathBuf::from("/home/user/data.json");
|
|
cesta.exists(); // existuje súbor?
|
|
cesta.is_file(); // je to súbor?
|
|
cesta.is_dir(); // je to priečinok?
|
|
cesta.extension(); // Some("json")
|
|
cesta.file_name(); // Some("data.json")
|
|
cesta.parent(); // Some("/home/user")
|
|
|
|
// Skladanie ciest
|
|
let mut cesta = PathBuf::from("/home/user");
|
|
cesta.push("dokumenty");
|
|
cesta.push("subor.txt");
|
|
// /home/user/dokumenty/subor.txt
|
|
```
|
|
|
|
### Na skúške — PathBuf v clape
|
|
|
|
```rust
|
|
use clap::Parser;
|
|
use std::path::PathBuf;
|
|
|
|
#[derive(Parser)]
|
|
struct Args {
|
|
#[arg(short, long, default_value = "data.json")]
|
|
subor: PathBuf,
|
|
}
|
|
|
|
fn main() {
|
|
let args = Args::parse();
|
|
// args.subor je PathBuf — predávaš priamo do funkcií
|
|
let kniznica = Kniznica::nacitaj_zo_suboru(&args.subor)
|
|
.unwrap_or_default();
|
|
}
|
|
```
|
|
|
|
### Funkcie berúce cestu
|
|
|
|
```rust
|
|
// Berie &PathBuf — funguje s PathBuf
|
|
fn nacitaj(cesta: &PathBuf) -> Option<String> {
|
|
std::fs::read_to_string(cesta).ok()
|
|
}
|
|
|
|
// Lepšie: berie &Path — funguje s PathBuf aj &str
|
|
fn nacitaj(cesta: &Path) -> Option<String> {
|
|
std::fs::read_to_string(cesta).ok()
|
|
}
|
|
|
|
// fs::read_to_string berie impl AsRef<Path> — prijme takmer čokoľvek
|
|
// Preto funguje:
|
|
fs::read_to_string("data.json"); // &str
|
|
fs::read_to_string(String::from("data.json")); // String
|
|
fs::read_to_string(PathBuf::from("data.json")); // PathBuf
|
|
fs::read_to_string(&pathbuf); // &PathBuf
|
|
```
|
|
|
|
### Úlohy 6
|
|
|
|
**6a.** Vytvor PathBuf z `"/home/user/docs"`. Pridaj `"projekt"` a `"data.json"`. Vypíš celú cestu.
|
|
|
|
**6b.** Máš PathBuf. Vypíš: existuje? Je súbor? Prípona? Názov súboru? Rodičovský priečinok?
|
|
|
|
**6c.** Prečo na skúške metóda `nacitaj_zo_suboru` berie `&PathBuf` a nie `&str`?
|
|
|
|
---
|
|
|
|
## Kapitola 7: serde JSON I/O — kompletný vzor
|
|
|
|
Toto je vzor na každej skúške. Kombinácia fs + serde_json.
|
|
|
|
```rust
|
|
use std::fs;
|
|
use std::path::PathBuf;
|
|
use serde::{Serialize, Deserialize};
|
|
|
|
#[derive(Serialize, Deserialize, Default)]
|
|
struct Kniznica {
|
|
knihy: Vec<Kniha>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone)]
|
|
struct Kniha {
|
|
nazov: String,
|
|
autor: String,
|
|
}
|
|
|
|
impl Kniznica {
|
|
// ČÍTANIE: súbor → String → štruktúra
|
|
fn nacitaj_zo_suboru(cesta: &PathBuf) -> Option<Kniznica> {
|
|
// 1. fs::read_to_string — súbor → String
|
|
let raw = fs::read_to_string(cesta).ok()?;
|
|
// 2. serde_json::from_str — String → štruktúra
|
|
serde_json::from_str(&raw).ok()
|
|
}
|
|
|
|
// ZÁPIS: štruktúra → String → súbor
|
|
fn uloz_do_suboru(&self, cesta: &PathBuf) -> bool {
|
|
// 1. serde_json::to_string_pretty — štruktúra → String
|
|
let Ok(json) = serde_json::to_string_pretty(&self) else {
|
|
return false;
|
|
};
|
|
// 2. fs::write — String → súbor
|
|
fs::write(cesta, json).is_ok()
|
|
}
|
|
}
|
|
```
|
|
|
|
Celý tok dát:
|
|
|
|
```
|
|
ČÍTANIE:
|
|
súbor na disku
|
|
→ fs::read_to_string → String s JSON textom
|
|
→ serde_json::from_str → Rust štruktúra
|
|
|
|
ZÁPIS:
|
|
Rust štruktúra
|
|
→ serde_json::to_string_pretty → String s JSON textom
|
|
→ fs::write → súbor na disku
|
|
```
|
|
|
|
### Použitie v main
|
|
|
|
```rust
|
|
fn main() {
|
|
let cesta = PathBuf::from("kniznica.json");
|
|
|
|
// Načítaj alebo vytvor prázdnu
|
|
let mut kniznica = Kniznica::nacitaj_zo_suboru(&cesta)
|
|
.unwrap_or_default();
|
|
|
|
// Urob niečo...
|
|
kniznica.knihy.push(Kniha {
|
|
nazov: "Duna".into(),
|
|
autor: "Herbert".into(),
|
|
});
|
|
|
|
// Ulož
|
|
kniznica.uloz_do_suboru(&cesta);
|
|
}
|
|
```
|
|
|
|
### Čo sa deje keď súbor neexistuje
|
|
|
|
```rust
|
|
// fs::read_to_string("neexistuje.json")
|
|
// → Err(io::Error)
|
|
// → .ok() → None
|
|
// → ? → vráti None z funkcie
|
|
|
|
// Preto:
|
|
Kniznica::nacitaj_zo_suboru(&PathBuf::from("neexistuje.json"))
|
|
// → None
|
|
|
|
// A v main:
|
|
let mut kniznica = Kniznica::nacitaj_zo_suboru(&cesta)
|
|
.unwrap_or_default();
|
|
// None → unwrap_or_default() → Kniznica::default() → Kniznica { knihy: vec![] }
|
|
```
|
|
|
|
### Úlohy 7
|
|
|
|
**7a.** Napíš `nacitaj_zo_suboru` a `uloz_do_suboru` pre:
|
|
```rust
|
|
#[derive(Serialize, Deserialize, Default)]
|
|
struct TodoList {
|
|
ulohy: Vec<Todo>,
|
|
}
|
|
#[derive(Serialize, Deserialize, Clone)]
|
|
struct Todo {
|
|
text: String,
|
|
hotova: bool,
|
|
}
|
|
```
|
|
|
|
**7b.** Napíš program, ktorý:
|
|
1. Načíta TodoList z "todo.json"
|
|
2. Pridá novú úlohu
|
|
3. Uloží späť
|
|
4. Znova načíta a vypíše počet úloh (overenie)
|
|
|
|
**7c.** Čo sa stane ak `serde_json::to_string_pretty` zlyhá? Kedy sa to stane? (Hint: takmer nikdy pre bežné štruktúry.)
|
|
|
|
---
|
|
|
|
## Kapitola 8: IOManager trait — vzor zo skúšky
|
|
|
|
Na skúške sa I/O abstrahuje cez trait. Toto umožňuje testovanie bez konzoly.
|
|
|
|
### Prečo nie priamo stdin?
|
|
|
|
```rust
|
|
// PROBLÉM: toto nejde testovať automaticky
|
|
fn hraj(hra: &mut Hra) {
|
|
let mut vstup = String::new();
|
|
io::stdin().read_line(&mut vstup).unwrap(); // čaká na konzolu
|
|
// ...
|
|
}
|
|
```
|
|
|
|
### Riešenie: IOManager trait
|
|
|
|
```rust
|
|
// 1. Definuj trait — AKO získať vstup
|
|
pub trait IOManager {
|
|
fn ziskaj_pismeno(&mut self) -> char;
|
|
}
|
|
|
|
// 2. Reálna implementácia — čítaj z konzoly
|
|
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(' ')
|
|
}
|
|
}
|
|
|
|
// 3. Metóda berie impl IOManager — funguje s čímkoľvek
|
|
impl Hra {
|
|
pub fn hraj(&mut self, mut io: impl IOManager) {
|
|
while !self.je_koniec_hry() {
|
|
println!("Slovo: {}", self.daj_slovo_skryto());
|
|
let pismeno = io.ziskaj_pismeno();
|
|
let vysledok = self.tipni_pismeno(pismeno);
|
|
println!("{}", vysledok);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 4. Volanie v main
|
|
fn main() {
|
|
let mut hra = Hra::new("rust");
|
|
hra.hraj(ConsoleIOManager); // predáme implementáciu
|
|
}
|
|
```
|
|
|
|
### ConsoleIOManager — prázdna štruktúra
|
|
|
|
```rust
|
|
// Prázdna štruktúra — žiadne členy
|
|
pub struct ConsoleIOManager;
|
|
|
|
// Toto nie je typ s poľami. Je to "unit struct".
|
|
// Vytváraš ju bez {} :
|
|
let io = ConsoleIOManager; // OK
|
|
// let io = ConsoleIOManager {}; // tiež OK ale zbytočné
|
|
```
|
|
|
|
### Rozšírený IOManager
|
|
|
|
Niekedy trait má viac metód:
|
|
|
|
```rust
|
|
pub trait IOManager {
|
|
fn ziskaj_pismeno(&mut self) -> char;
|
|
fn zobraz_spravu(&self, sprava: &str);
|
|
fn zobraz_stav(&self, zivoty: u8, slovo: &str, skusane: &[char]);
|
|
}
|
|
|
|
pub struct ConsoleIOManager;
|
|
|
|
impl IOManager for ConsoleIOManager {
|
|
fn ziskaj_pismeno(&mut self) -> char {
|
|
print!("Zadaj písmeno: ");
|
|
let mut vstup = String::new();
|
|
std::io::stdin().read_line(&mut vstup).unwrap();
|
|
vstup.trim().chars().next().unwrap_or(' ')
|
|
}
|
|
|
|
fn zobraz_spravu(&self, sprava: &str) {
|
|
println!("{}", sprava);
|
|
}
|
|
|
|
fn zobraz_stav(&self, zivoty: u8, slovo: &str, skusane: &[char]) {
|
|
println!("Životov: {}", zivoty);
|
|
println!("Slovo: {}", slovo);
|
|
println!("Skúšané: {:?}", skusane);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Úlohy 8
|
|
|
|
**8a.** Napíš trait `Vstup` s metódou `nacitaj_cislo(&mut self) -> Option<i32>`. Implementuj:
|
|
- `KonzolaVstup` — číta z stdin
|
|
- `TestovaciVstup { cisla: Vec<i32>, index: usize }` — vracia predpripravené čísla
|
|
|
|
**8b.** Napíš funkciu `hadaj_cislo(tajne: i32, mut vstup: impl Vstup)` — hra na hádanie čísla. Používa trait namiesto priameho stdin.
|
|
|
|
**8c.** Máš IOManager z príkladu vyššie. Napíš `TestIOManager`:
|
|
```rust
|
|
struct TestIOManager {
|
|
odpovede: Vec<char>,
|
|
index: usize,
|
|
}
|
|
```
|
|
Implementuj IOManager — `ziskaj_pismeno` vráti ďalšie písmeno z `odpovede`.
|
|
|
|
---
|
|
|
|
## Kapitola 9: flush — print! bez nového riadku
|
|
|
|
`print!` nevyprázdni buffer automaticky. Ak chceš aby sa text zobrazil okamžite pred čítaním vstupu:
|
|
|
|
```rust
|
|
use std::io::{self, Write};
|
|
|
|
fn main() {
|
|
print!("Zadaj meno: ");
|
|
io::stdout().flush().unwrap(); // DÔLEŽITÉ! Vynúti výpis
|
|
|
|
let mut vstup = String::new();
|
|
io::stdin().read_line(&mut vstup).unwrap();
|
|
println!("Ahoj, {}!", vstup.trim());
|
|
}
|
|
```
|
|
|
|
Bez `flush()` sa "Zadaj meno: " môže zobraziť až po tom, čo zadáš vstup. S `flush()` sa zobrazí hneď.
|
|
|
|
```rust
|
|
// Kompletný vzor pre prompt
|
|
fn prompt(text: &str) -> String {
|
|
print!("{}", text);
|
|
io::stdout().flush().unwrap();
|
|
let mut vstup = String::new();
|
|
io::stdin().read_line(&mut vstup).unwrap();
|
|
vstup.trim().to_string()
|
|
}
|
|
|
|
// Použitie:
|
|
let meno = prompt("Meno: ");
|
|
let vek = prompt("Vek: ");
|
|
```
|
|
|
|
### Úlohy 9
|
|
|
|
**9a.** Napíš funkciu `prompt(text: &str) -> String` s flush. Použi ju na prečítanie mena a veku.
|
|
|
|
**9b.** Napíš funkciu `prompt_cislo(text: &str) -> Option<i32>` — zobrazí text, prečíta, parsne.
|
|
|
|
**9c.** Napíš interaktívny program, ktorý používa prompt funkciu na čítanie príkazov v cykle.
|
|
|
|
---
|
|
|
|
## Kapitola 10: Prehľad a vzory
|
|
|
|
### Rýchly prehľad: čo kedy použiť
|
|
|
|
| Chcem... | Použi |
|
|
|----------|-------|
|
|
| Čítať celý súbor | `fs::read_to_string(cesta)` |
|
|
| Zapísať celý súbor | `fs::write(cesta, obsah)` |
|
|
| Čítať po riadkoch (malý súbor) | `fs::read_to_string` + `.lines()` |
|
|
| Čítať po riadkoch (veľký súbor) | `BufReader::new(File::open)` + `.lines()` |
|
|
| Pripísať na koniec | `OpenOptions::new().append(true)` |
|
|
| Čítať z konzoly | `io::stdin().read_line(&mut buf)` |
|
|
| Prompt bez newline | `print!()` + `io::stdout().flush()` |
|
|
| JSON: súbor → štruktúra | `fs::read_to_string` + `serde_json::from_str` |
|
|
| JSON: štruktúra → súbor | `serde_json::to_string_pretty` + `fs::write` |
|
|
| Testovateľný I/O | `trait IOManager` + `impl IOManager for ...` |
|
|
|
|
### Vzor: kompletné menu programu
|
|
|
|
```rust
|
|
use std::io::{self, Write};
|
|
use std::path::PathBuf;
|
|
|
|
fn prompt(text: &str) -> String {
|
|
print!("{}", text);
|
|
io::stdout().flush().unwrap();
|
|
let mut vstup = String::new();
|
|
io::stdin().read_line(&mut vstup).unwrap();
|
|
vstup.trim().to_string()
|
|
}
|
|
|
|
fn main() {
|
|
let cesta = PathBuf::from("data.json");
|
|
let mut kniznica = Kniznica::nacitaj_zo_suboru(&cesta)
|
|
.unwrap_or_default();
|
|
|
|
loop {
|
|
println!("\n--- KNIŽNICA ---");
|
|
println!("1. Pridaj knihu");
|
|
println!("2. Vypíš knihy");
|
|
println!("3. Hľadaj");
|
|
println!("4. Koniec");
|
|
|
|
let volba = prompt("Vyber: ");
|
|
|
|
match volba.as_str() {
|
|
"1" => {
|
|
let nazov = prompt("Názov: ");
|
|
let autor = prompt("Autor: ");
|
|
// ... pridaj knihu
|
|
kniznica.uloz_do_suboru(&cesta);
|
|
println!("Pridané.");
|
|
}
|
|
"2" => {
|
|
for kniha in &kniznica.knihy {
|
|
println!("{}", kniha);
|
|
}
|
|
}
|
|
"3" => {
|
|
let vyraz = prompt("Hľadaj: ");
|
|
// ... hľadaj
|
|
}
|
|
"4" => {
|
|
println!("Zbohom!");
|
|
break;
|
|
}
|
|
_ => println!("Neznáma voľba"),
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Úlohy 10
|
|
|
|
**10a.** Napíš kompletný interaktívny program pre TODO list:
|
|
- Pridaj úlohu (prompt názov)
|
|
- Označ ako hotovú (prompt index)
|
|
- Vypíš všetky
|
|
- Ulož do JSON
|
|
- Koniec
|
|
|
|
**10b.** Napíš program, ktorý prečíta CSV súbor (meno,vek,mesto), sparsuje riadky do Vec štruktúr, a umožní hľadanie podľa mesta.
|
|
|
|
**10c.** Implementuj kompletný IOManager trait pre hru Obesenec. Vytvor aj TestIOManager, ktorý automaticky „hrá" hru s predpripravenými písmenami.
|