Obsah
2. Čtení jednotlivých bajtů z binárního souboru
3. Přesun „kurzoru“ v rámci aktuálně otevřeného souboru
4. Serializace a deserializace dat
6. Příprava projektu řízeného nástrojem Cargo
7. První projekt: serializace a deserializace hodnoty typu i32
8. Druhý projekt: korektní reakce na chyby, které by mohly při serializaci či deserializaci nastat
9. Přímá serializace do souboru
10. Třetí projekt: přímá serializace do souboru
11. Přímá deserializace ze souboru
12. Čtvrtý projekt: přímá deserializace ze souboru
13. Repositář s demonstračními příklady a projekty
1. Práce s binárními soubory
V předchozím dílu seriálu o programovacím jazyku Rust jsme se seznámili se základními traity a strukturami používanými při práci s datovými streamy, tj. například se soubory či se standardními streamy (stdin, stdout, stderr). Připomeňme si, že základními traity pro vstupně-výstupní operace jsou traity std::io::Read a std::io::Write. Tyto traity jsou mj. implementovány i strukturami std::fs::File (zápis i čtení ze souborů), std::io::Stdin (jen čtení), std::io::Stdout (jen zápis) a samozřejmě také std::io::Stderr (opět jen zápis).
Pro bufferovaný vstup se používá struktura std::io::BufReader (ta mj. umožňuje pracovat se souborem jako s iterátorem vracejícím jednotlivé řádky) a pro bufferovaný výstup se používá struktura std::io::BufWriter. Tyto struktury se v praxi používají velmi často, protože je práce s nimi většinou efektivnější než nízkoúrovňový přístup přes traity Read a Write.
V dalším textu se budeme zabývat jak prací s binárními soubory, tak i problematikou serializace a deserializace. Ta ovšem není řešena standardní knihovnou, ale dalšími přídavnými moduly.
2. Čtení jednotlivých bajtů z binárního souboru
Traity Read a Write pracují na úrovni jednotlivých bajtů popř. řezů polí a vektorů obsahujících bajty. Pouze v případě čtení je možné načíst celý řetězec v kódování UTF-8 (pokud vstupní soubor obsahuje neplatné kombinace bajtů, vrátí se chyba). Konkrétně se jedná o následující funkce:
Trait | Funkce | Použitý typ | Návratová hodnota |
---|---|---|---|
Write | write | &[u8] | void |
Read | read | &[u8] | void |
Read | read_to_end | Vec<u8> | Result<usize> |
Read | read_to_string | String | Result<usize> |
Pokud budete potřebovat postupně načítat jednotlivé bajty (což je neefektivní), je možné použít například následující funkci s jednobajtovým bufferem:
fn read_one_byte(mut fin: &File) -> bool { let mut buffer = [0; 1]; match fin.read(&mut buffer) { Ok(size) => { if size > 0 { println!("Read: '{}' = {}", buffer[0] as char, buffer[0]); } size > 0 } Err(error) => { println!("file read error: {}", error); false } } }
Zajímavé je, že přesný typ bufferu, tedy že se jedná o buffer s jedinou položkou typu u8, si překladač zjistí až z řádku, kde se provádí volání fin.read(). Zajímavý je i řádek s přetypováním bajtu na znak buffer[0] as char, což je zde nutné, aby makro println! skutečně do apostrofů vypsalo jednotlivé znaky a nikoli jejich kódy. Funkce samozřejmě bude korektně pracovat pouze pro (rozšířené) ASCII soubory.
Poznámka: povšimněte si, že díky použití typu Result a pattern matchingu není nutné, aby funkce read vracela „speciální“ hodnoty typu EOF atd., podobně jako to známe například z céčkovské getc apod.
Následující příklad nejdříve vytvoří textový soubor „test.txt“ a následně z tohoto souboru postupně načte jednotlivé bajty:
use std::io::Read; use std::io::Write; use std::fs::File; fn write_message_to_file(mut fout: &File) { match fout.write(b"Hello, world!\n") { Ok(written) => { println!("{} bytes written", written); } Err(error) => { println!("write error: {}", error); } } } fn create_hello_world_file(file_name: &str) { match File::create(file_name) { Ok(fout) => { write_message_to_file(&fout); } Err(error) => { println!("file create error: {}", error); } } } fn read_one_byte(mut fin: &File) -> bool { let mut buffer = [0; 1]; match fin.read(&mut buffer) { Ok(size) => { if size > 0 { println!("Read: '{}' = {}", buffer[0] as char, buffer[0]); } size > 0 } Err(error) => { println!("file read error: {}", error); false } } } fn main() { create_hello_world_file("test.txt"); let fin = File::open("test.txt").unwrap(); while read_one_byte(&fin) {} }
Tento program by měl po svém spuštění na standardní výstup vypsat následující řádky (poslední přečtený znak je znakem pro konec řádku, proto je výstup „rozházený“):
14 bytes written Read: 'H' = 72 Read: 'e' = 101 Read: 'l' = 108 Read: 'l' = 108 Read: 'o' = 111 Read: ',' = 44 Read: ' ' = 32 Read: 'w' = 119 Read: 'o' = 111 Read: 'r' = 114 Read: 'l' = 108 Read: 'd' = 100 Read: '!' = 33 Read: ' ' = 10
3. Přesun „kurzoru“ v rámci aktuálně otevřeného souboru
Při práci se soubory, tj. se strukturou std::fs::File, je možné se díky implementaci traitu std::io::Seek posouvat v otevřeném souboru na libovolnou pozici. Pokud například otevřeme testovací textový soubor „test.txt“ pro čtení:
let mut fin = File::open("test.txt").unwrap();
je možné se posouvat relativně vůči začátku souboru, konci souboru (zde je nutné použít záporný offset!) či vůči aktuální pozici imaginárního „kurzoru“ v souboru. Zápis je jednoduchý:
// posun vůči začátku souboru (sedmý bajt) // lze se posunout ZA konec souboru, ovšem chování je systémově závislé let position = fin.seek(SeekFrom::Start(7)); // posun vůči konci souboru (druhý bajt od konce) // lze se posunout ZA konec souboru, nikoli PŘED jeho začátek let position = fin.seek(SeekFrom::End(-2)); // posun relativně vůči aktuální pozici // lze se posunout ZA konec souboru, nikoli PŘED jeho začátek let position = fin.seek(SeekFrom::Current(-2));
Metoda seek vrátí aktuální (novou) pozici v souboru. Podívejme se nyní na jednoduchý demonstrační příklad:
use std::io::Read; use std::io::Write; use std::io::Seek; use std::io::SeekFrom; use std::fs::File; use std::io::Error; fn write_message_to_file(mut fout: &File) { match fout.write(b"Hello, world!\n") { Ok(written) => { println!("{} bytes written", written); } Err(error) => { println!("write error: {}", error); } } } fn create_hello_world_file(file_name: &str) { match File::create(file_name) { Ok(fout) => { write_message_to_file(&fout); } Err(error) => { println!("file create error: {}", error); } } } fn read_one_byte(mut fin: &File) -> bool { let mut buffer = [0; 1]; match fin.read(&mut buffer) { Ok(size) => { if size > 0 { println!("Read: '{}' = {}", buffer[0] as char, buffer[0]); } size > 0 } Err(error) => { println!("file read error: {}", error); false } } } fn print_position(pos: Result<u64, Error>) { println!("position in file: {}", pos.unwrap()); } fn main() { create_hello_world_file("test.txt"); let mut fin = File::open("test.txt").unwrap(); read_one_byte(&fin); let position = fin.seek(SeekFrom::Start(7)); read_one_byte(&fin); print_position(position); let position = fin.seek(SeekFrom::End(-2)); read_one_byte(&fin); print_position(position); let position = fin.seek(SeekFrom::Current(-2)); read_one_byte(&fin); print_position(position); }
Tento program by měl po svém spuštění na standardní výstup vypsat následující řádky s přečtenými bajty a taktéž s aktuálně nastavenou pozicí v souboru:
14 bytes written Read: 'H' = 72 Read: 'w' = 119 position in file: 7 Read: '!' = 33 position in file: 12 Read: 'd' = 100 position in file: 11
4. Serializace a deserializace dat
Ve druhé části dnešního článku se budeme zabývat problematikou serializace a deserializace dat. Tato činnost není ve standardní knihovně přímo podporovaná, takže je nutné použít nějakou další knihovnu popř. si napsat vlastní řešení (to bude ovšem pravděpodobně ošklivé a obecně nebezpečné, protože se možná nevyhnete nutnosti použití takzvaných transmutací). Pro začátek si řekneme základní informace o serializaci do binárních souborů, k čemuž se dnes pravděpodobně nejčastěji používá knihovna bincode. V případě potřeby je možné tuto knihovnu doplnit o knihovny Byteorder (převody hodnot mezi little a bit endian a naopak) a Serde (serializing and deserializing), jejíž možnosti si popíšeme příště.
Poznámka: v minulosti se používala i knihovna rustc-serialize, ta již ovšem není podporovaná, protože přednost dostala knihovna Serde, která je dnes v této oblasti de facto standardem.
5. Použití modulu bincode
V knihovně bincode nalezneme několik pomocných struktur, výčtových typů a především pak následující čtveřici funkcí:
Funkce | Význam |
---|---|
serialize | serializace objektu do vektoru bajtů |
deserialize | deserializace řezu polem bajtů do zvoleného typu objektu |
serialize_into | serializace přímo do writeru (typicky do souboru) |
deserialize_from | deserializace z readeru (typicky ze souboru) |
Teoreticky je možné si vystačit s těmito čtyřmi funkcemi, v praxi však bude nutné pro uživatelské struktury implementovat traity Serialize a Deserialize.
6. Příprava projektu řízeného nástrojem Cargo
Pro použití modulu bincode si vytvoříme nový projekt, a to s využitím velmi užitečného nástroje Cargo. Tento nástroj již byl stručně popsán na konkurenčním :-) serveru, takže si dnes jen ve stručnosti řekneme, jak se projekt vytvoří a jak se následně zkonfiguruje.
Pro vytvoření nového projektu postačuje použít příkaz cargo new, kterému se předá jméno projektu (to je povinné). Pokud se ovšem má vytvořit projekt, jehož výsledkem bude spustitelná aplikace (což je i náš případ), je nutné navíc použít přepínač –-bin. V opačném případě by se totiž vytvořil projekt s kostrou knihovny. Vytvořme si nyní jednoduchý projekt nazvaný „serialization1“, po jehož překladu získáme spustitelnou aplikaci (a nikoli knihovnu):
cargo new --bin serialiation Created binary (application) `serialiation` project
Struktura projektu by měla vypadat následovně – jedná se o jeden projektový soubor nazvaný Cargo.toml a o jeden zdrojový soubor umístěný v podadresáři src:
. ├── Cargo.toml └── src └── main.rs 1 directory, 2 files
Z hlediska nástroje Cargo je nejdůležitější projektový soubor pojmenovaný jednoduše Cargo.toml. Již víme, že tento soubor je vytvořen automaticky při inicializaci projektu, ovšem předpokládá se, že ho bude programátor měnit v závislosti na potřebách konkrétního projektu. My si tento soubor upravíme tak, že do závislostí přidáme modul (crate) nazvaný bincode:
[package] name = "serialization1" version = "0.1.0" authors = ["Pavel Tisnovsky <ptisnovs@redhat.com>"] [dependencies] bincode = "0.7.0"
Příkazem cargo build můžeme spustit proces, který stáhne všechny potřebné knihovny a následně projekt přeloží.
7. První projekt: serializace a deserializace hodnoty typu i32
Použití funkcí serialize() a deserialize() si ukážeme na velmi jednoduchém příkladu, v němž nejdříve serializujeme celé číslo typu i32 (integer) do vektoru čtyř bajtů a následně provedeme deserializaci. Návratovými hodnotami obou zmíněných funkcí je Result<typ>, takže pro jednoduchost použijeme metodu unwrap() na získání vrácené hodnoty. Budeme tedy předpokládat, že při serializaci a deserializaci nedojde k žádné chybě (což je poněkud optimistické):
#[macro_use] extern crate bincode; use bincode::{serialize, deserialize, Infinite}; fn main() { let x:i32 = 0x12345678; let serialized: Vec<u8> = serialize(&x, Infinite).unwrap(); for i in &serialized { println!("{:x}", i); } let deserialized: i32 = deserialize(&serialized[..]).unwrap(); println!("deserialized value: 0x{:x}", deserialized); }
Hodnota Infinite slouží pro určení maximální povolené velikosti výsledného vektoru bajtů (zde samozřejmě bez omezení).
Povšimněte si, že při volání funkce deserialize() musíme překladači pomoci se zjištěním typu deserializovaného objektu. To je ostatně logické, protože překladač nemá žádnou jinou možnost, jak ze sekvence bajtů zjistit, o jaký objekt se jednalo.
Při prvním spuštění se stáhnou všechny potřebné knihovny:
Updating registry `https://github.com/rust-lang/crates.io-index` Downloading bincode v0.7.0 Downloading num-traits v0.1.37 Downloading byteorder v1.0.0 Downloading serde v0.9.15 Compiling num-traits v0.1.37 Compiling byteorder v1.0.0 Compiling serde v0.9.15 Compiling bincode v0.7.0 Compiling serialization1 v0.1.0 (file:///home/tester/temp/presentations/rust/new/serialization1) Finished debug [unoptimized + debuginfo] target(s) in 41.77 secs Running `target/debug/serialization1`
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs Running `target/debug/serialization1` 78 56 34 12 deserialized value: 0x12345678
8. Druhý projekt: korektní reakce na chyby, které by mohly při serializaci či deserializaci nastat
Druhý projekt, který je opět vytvořený přes nástroj Cargo, pouze vylepšuje projekt předchozí, protože zde již reagujeme na všechny problémy, s nimiž se můžeme při serializaci a deserializaci setkat. Podobně jako u mnoha předchozích příkladů se i zde snažíme používat pattern matching namísto otrockého psaní podmínek if-else s explicitním přístupem do struktury Result. Opět si povšimněte, že jsme ve větvi Ok(deserialed) nuceni překladači „pomoci“ uvedením typu deserializovaného objektu s použitím pomocné proměnné:
#[macro_use] extern crate bincode; use bincode::{serialize, deserialize, Infinite}; fn main() { let x:i32 = 0x12345678; match serialize(&x, Infinite) { Ok(encoded) => { for i in &encoded { println!("{:x}", i); } match deserialize(&encoded) { Ok(deserialized) => { let val:i32 = deserialized; println!("deserialized value: 0x{:x}", val); } Err(error) => { println!("deserialization error: {}", error); } } } Err(error) => { println!("serialization error: {}", error); } } }
Překlad a spuštění tohoto projektu bude vypadat následovně:
Compiling serialization2 v0.1.0 (file:///home/tester/temp/presentations/rust/new/serialization2) Finished debug [unoptimized + debuginfo] target(s) in 0.80 secs Running `target/debug/serialization2` 78 56 34 12 deserialized value: 0x12345678
9. Přímá serializace do souboru
S využitím funkce serialize_into() je možné serializovaný objekt přímo uložit do souboru. To může být velmi výhodné, neboť knihovna bincode může serializaci provádět postupně, dokonce ani nemusí v operační paměti vytvářet serializovaný obraz objektu, což se projeví především u obrovských objektů (představme si například dvourozměrné pole s texturou či rastrovým obrázkem atd.). Serializaci přímo do souboru provedeme takto:
fn serialize_value(mut fout: &File, value: i32) { match serialize_into(&mut fout, &value, Infinite) { Ok(_) => { println!("successfully serialized into file"); } Err(error) => { println!("serialization error: {}", error); } } }
Poznámka: soubor musí být samozřejmě otevřen v režimu zápisu. Hodnota Infinite opět instruuje knihovnu bincode, aby nijak neomezovala velikost výsledného proudu bajtů.
10. Třetí projekt: přímá serializace do souboru
Výše uvedenou funkci serialize_value() použijeme ve třetím projektu, který po svém spuštění vytvoří soubor nazvaný „test.bin“. Tento soubor by měl mít velikost přesně čtyři bajty a měl by obsahovat binární podobu celého čísla 0×12345678, decimálně tedy 305419896:
#[macro_use] extern crate bincode; use std::fs::File; use bincode::{serialize_into, Infinite}; fn serialize_value(mut fout: &File, value: i32) { match serialize_into(&mut fout, &value, Infinite) { Ok(_) => { println!("successfully serialized into file"); } Err(error) => { println!("serialization error: {}", error); } } } fn main() { let x:i32 = 0x12345678; match File::create("test.bin") { Ok(fout) => { serialize_value(&fout, x); } Err(error) => { println!("file create error: {}", error); } } }
Po zadání příkazu cargo run by se měla vypsat zpráva o úspěšném překladu a vytvoření binárního souboru (cesta k vytvořenému spustitelnému souboru bude samozřejmě odlišná do cesty získané na testovacím stroji):
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs Running `/home/tester/temp/presentations/rust/new/serialization3/target/debug/serialization3` successfully serialized into file
Vytvořený binární soubor si můžeme velmi snadno prohlédnout nástrojem od nebo hexdump:
hexdump -C test.bin 00000000 78 56 34 12 |xV4.| 00000004
Poznámka: v tomto případě by bylo vhodnější použít modul byteorder a explicitně specifikovat, v jakém pořadí se mají bajty reprezentující 32bitové celé číslo uložit. Podrobnosti si ukážeme příště (tento modul je navíc velmi jednoduše použitelný).
11. Přímá deserializace ze souboru
Opakem přímé serializace dat do souboru je samozřejmě přímá deserializace. To opět znamená, že není zapotřebí, aby se obsah souboru nejdříve explicitně načetl do pole či vektoru bajtů a následně se provedla deserializace. Obě činnosti provede funkce nazvaná deserialize_from(), která vynechá mezikrok s načtením celého souboru (což by v některých případech ani nebylo možné a už vůbec ne praktické). Podívejme se nyní na velmi jednoduchou funkci provádějící deserializaci čtyř bajtů tvořících jedno celé číslo typu i32. Překladač zná návratovou hodnotu funkce a z ní si odvodí typ, který se má deserializovat. Jak však můžete vidět, není odvození triviální, neboť vlastně říkáme: potřebujeme hodnotu typu i32 obalenou typem Option, která se získá z typu Result<i32> a přesně tento typ vyžaduj jak návratovou hodnotu generické funkce deserialize_from():
fn deserialize_value(mut fin: &File) -> Option<i32> { match deserialize_from(&mut fin, Infinite) { Ok(value) => { println!("successfully deserialized from file"); Some(value) } Err(error) => { println!("serialization error: {}", error); None } } }
12. Čtvrtý projekt: přímá deserializace ze souboru
Výše popsanou funkci přidáme do dnešního posledního projektu, do jehož adresáře si však nejdříve překopírujte soubor „test.bin“ vytvořený předchozím projektem (pokud to neuděláte, aspoň si můžete otestovat, do jaké míry se v programu pracuje s chybovými stavy):
#[macro_use] extern crate bincode; use std::fs::File; use bincode::{deserialize_from, Infinite}; fn deserialize_value(mut fin: &File) -> Option<i32> { match deserialize_from(&mut fin, Infinite) { Ok(value) => { println!("successfully deserialized from file"); Some(value) } Err(error) => { println!("serialization error: {}", error); None } } } fn main() { match File::open("test.bin") { Ok(fin) => { match deserialize_value(&fin) { Some(value) => { println!("deserialized value: 0x{:x}", value); } None => { } } } Err(error) => { println!("file create error: {}", error); } } }
Pokud byl soubor „test.bin“ nalezen a pokud skutečně obsahuje čtyři bajty 0×78, 0×56, 0×34 a 0×12, měl by tento příklad vypsat na standardní výstup následující text:
successfully deserialized from file deserialized value: 0x12345678
13. Repositář s demonstračními příklady a projekty
Všechny dnes popisované demonstrační příklady a projekty byly, ostatně podobně jako ve všech předchozích částech tohoto seriálu, uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/presentations. Příklady a projekty si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý repositář:
14. Odkazy na Internetu
- Cargo: správce projektů a balíčků pro programovací jazyk Rust
https://mojefedora.cz/cargo-spravce-projektu-a-balicku-pro-programovaci-jazyk-rust/ - Network Communication and Serialization in Rust
https://www.safaribooksonline.com/blog/2014/01/28/network-communication-serialization-rust/ - Crate bincode
http://tyoverby.com/bincode/bincode/index.html - Struct std::fs::File
https://doc.rust-lang.org/std/fs/struct.File.html - Trait std::io::Seek
https://doc.rust-lang.org/std/io/trait.Seek.html - Trait std::io::Read
https://doc.rust-lang.org/std/io/trait.Read.html - Trait std::io::Write
https://doc.rust-lang.org/std/io/trait.Write.html - Trait std::io::BufRead
https://doc.rust-lang.org/std/io/trait.BufRead.html - Module std::io::prelude
https://doc.rust-lang.org/std/io/prelude/index.html - std::net::IpAddr
https://doc.rust-lang.org/std/net/enum.IpAddr.html - std::net::Ipv4Addr
https://doc.rust-lang.org/std/net/struct.Ipv4Addr.html - std::net::Ipv6Addr
https://doc.rust-lang.org/std/net/struct.Ipv6Addr.html - TcpListener
https://doc.rust-lang.org/std/net/struct.TcpListener.html - TcpStream
https://doc.rust-lang.org/std/net/struct.TcpStream.html - Binary heap (Wikipedia)
https://en.wikipedia.org/wiki/Binary_heap - Binární halda (Wikipedia)
https://cs.wikipedia.org/wiki/Bin%C3%A1rn%C3%AD_halda - Halda (datová struktura)
https://cs.wikipedia.org/wiki/Halda_%28datov%C3%A1_struktura%29 - Struct std::collections::HashSet
https://doc.rust-lang.org/std/collections/struct.HashSet.html - Struct std::collections::BTreeSet
https://doc.rust-lang.org/std/collections/struct.BTreeSet.html - Struct std::collections::BinaryHeap
https://doc.rust-lang.org/std/collections/struct.BinaryHeap.html - Set (abstract data type)
https://en.wikipedia.org/wiki/Set_%28abstract_data_type%29#Language_support - Associative array
https://en.wikipedia.org/wiki/Associative_array - Hash Table
https://en.wikipedia.org/wiki/Hash_table - B-tree
https://en.wikipedia.org/wiki/B-tree - Pedro Celis: Robin Hood Hashing (naskenované PDF!)
https://cs.uwaterloo.ca/research/tr/1986/CS-86–14.pdf - Robin Hood hashing
http://codecapsule.com/2013/11/11/robin-hood-hashing/ - Robin Hood hashing: backward shift deletion
http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/ - Module std::collections
https://doc.rust-lang.org/std/collections/ - Module std::vec
https://doc.rust-lang.org/nightly/std/vec/index.html - Struct std::collections::VecDeque
https://doc.rust-lang.org/std/collections/struct.VecDeque.html - Struct std::collections::LinkedList
https://doc.rust-lang.org/std/collections/struct.LinkedList.html - Module std::fmt
https://doc.rust-lang.org/std/fmt/ - Macro std::println
https://doc.rust-lang.org/std/macro.println.html - Enum std::result::Result
https://doc.rust-lang.org/std/result/enum.Result.html - Module std::result
https://doc.rust-lang.org/std/result/ - Result
http://rustbyexample.com/std/result.html - Rust stdlib: Option
https://doc.rust-lang.org/std/option/enum.Option.html - Module std::option
https://doc.rust-lang.org/std/option/index.html - Rust by example: option
http://rustbyexample.com/std/option.html - Rust by example: if-let
http://rustbyexample.com/flow_control/if_let.html - Rust by example: while let
http://rustbyexample.com/flow_control/while_let.html - Rust by example: Option<i32>
http://rustbyexample.com/std/option.html - An Overview of Macros in Rust
http://words.steveklabnik.com/an-overview-of-macros-in-rust - A Practical Intro to Macros in Rust 1.0
https://danielkeep.github.io/practical-intro-to-macros.html - The Rust Programming Language: macros
https://doc.rust-lang.org/beta/book/macros.html - Rust by example: 15 macro_rules!
http://rustbyexample.com/macros.html - Primitive Type isize
https://doc.rust-lang.org/nightly/std/primitive.isize.html - Primitive Type usize
https://doc.rust-lang.org/nightly/std/primitive.usize.html - Primitive Type array
https://doc.rust-lang.org/nightly/std/primitive.array.html - Module std::slice
https://doc.rust-lang.org/nightly/std/slice/ - Rust by Example: 2.3 Arrays and Slices
http://rustbyexample.com/primitives/array.html - What is the difference between Slice and Array (stackoverflow)
http://stackoverflow.com/questions/30794235/what-is-the-difference-between-slice-and-array - Learning Rust With Entirely Too Many Linked Lists
http://cglab.ca/~abeinges/blah/too-many-lists/book/ - Testcase: linked list
http://rustbyexample.com/custom_types/enum/testcase_linked_list.html - Operators and Overloading
https://doc.rust-lang.org/book/operators-and-overloading.html - Module std::ops
https://doc.rust-lang.org/std/ops/index.html - Module std::cmp
https://doc.rust-lang.org/std/cmp/index.html - Trait std::ops::Add
https://doc.rust-lang.org/stable/std/ops/trait.Add.html - Trait std::ops::AddAssign
https://doc.rust-lang.org/std/ops/trait.AddAssign.html - Trait std::ops::Drop
https://doc.rust-lang.org/std/ops/trait.Drop.html - Trait std::cmp::Eq
https://doc.rust-lang.org/std/cmp/trait.Eq.html - Struct std::boxed::Box
https://doc.rust-lang.org/std/boxed/struct.Box.html - Explore the ownership system in Rust
https://nercury.github.io/rust/guide/2015/01/19/ownership.html - Rust's ownership and move semantic
http://www.slideshare.net/saneyuki/rusts-ownership-and-move-semantics - Trait std::marker::Copy
https://doc.rust-lang.org/stable/std/marker/trait.Copy.html - Trait std::clone::Clone
https://doc.rust-lang.org/stable/std/clone/trait.Clone.html - The Stack and the Heap
https://doc.rust-lang.org/book/the-stack-and-the-heap.html - Rust Compare: Pointers & References
http://www.rust-compare.com/site/pointers.html - Rust Compare: Parameters
http://www.rust-compare.com/site/params.html - Why does this compile? Automatic dereferencing?
https://users.rust-lang.org/t/why-does-this-compile-automatic-dereferencing/2183 - Understanding Pointers, Ownership, and Lifetimes in Rust
http://koerbitz.me/posts/Understanding-Pointers-Ownership-and-Lifetimes-in-Rust.html - Rust lang series episode #25 — pointers (#rust-series)
https://steemit.com/rust-series/@jimmco/rust-lang-series-episode-25-pointers-rust-series - Rust – home page
https://www.rust-lang.org/en-US/ - Rust – Frequently Asked Questions
https://www.rust-lang.org/en-US/faq.html - Destructuring and Pattern Matching
https://pzol.github.io/getting_rusty/posts/20140417_destructuring_in_rust/ - The Rust Programming Language
https://doc.rust-lang.org/book/ - Rust (programming language)
https://en.wikipedia.org/wiki/Rust_%28programming_language%29 - Go – home page
https://golang.org/ - Stack Overflow – Most Loved, Dreaded, and Wanted language
https://stackoverflow.com/research/developer-survey-2016#technology-most-loved-dreaded-and-wanted - Rust vs Go (dva roky staré hodnocení, od té doby došlo k posunům v obou jazycích)
http://jaredforsyth.com/2014/03/22/rust-vs-go/ - Rust vs Go: My experience
https://www.reddit.com/r/golang/comments/21m6jq/rust_vs_go_my_experience/ - Friends of Rust (Organizations running Rust in production)
https://www.rust-lang.org/en-US/friends.html - Rust programs versus C++ g++
https://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=rust&lang2=gpp - Další benchmarky (nejedná se o reálné příklady „ze života“)
https://github.com/kostya/benchmarks - Go na Redditu
https://www.reddit.com/r/golang/ - Rust vs. Go
http://vschart.com/compare/rust/vs/go-language - Abstraction without overhead: traits in Rust
https://blog.rust-lang.org/2015/05/11/traits.html - Method Syntax
https://doc.rust-lang.org/book/method-syntax.html - Traits in Rust
https://doc.rust-lang.org/book/traits.html - Functional Programming in Rust – Part 1 : Function Abstraction
http://blog.madhukaraphatak.com/functional-programming-in-rust-part-1/ - Of the emerging systems languages Rust, D, Go and Nim, which is the strongest language and why?
https://www.quora.com/Of-the-emerging-systems-languages-Rust-D-Go-and-Nim-which-is-the-strongest-language-and-why - Chytré ukazatele (moderní verze jazyka C++) [MSDN]
https://msdn.microsoft.com/cs-cz/library/hh279674.aspx - UTF-8 Everywhere
http://utf8everywhere.org/ - Rust by Example
http://rustbyexample.com/ - Rust oficiálně ve Fedoře
https://mojefedora.cz/rust-oficialne-ve-fedore/ - Resource acquisition is initialization
https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization - TIOBE index (October 2016)
http://www.tiobe.com/tiobe-index/ - Porovnání Go, D a Rustu na OpenHubu:
https://www.openhub.net/languages/compare?language_name[]=-1&language_name[]=-1&language_name[]=dmd&language_name[]=golang&language_name[]=rust&language_name[]=-1&measure=commits - String Types in Rust
http://www.suspectsemantics.com/blog/2016/03/27/string-types-in-rust/ - Trait (computer programming)
https://en.wikipedia.org/wiki/Trait_%28computer_programming%29 - Type inference
https://en.wikipedia.org/wiki/Type_inference