Hlavní navigace

Programovací jazyk Rust: práce s binárními soubory, serializace a deserializace

Pavel Tišnovský

V další části seriálu o jazyku Rust se seznámíme se základními a prozatím velmi jednoduchými koncepty práce s binárními soubory a taktéž s problematikou serializace a deserializace dat s projektem bincode.

Obsah

1. Práce s binárními soubory

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

5. Použití modulu bincode

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

14. Odkazy na Internetu

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/pre­sentations. 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

  1. Cargo: správce projektů a balíčků pro programovací jazyk Rust
    https://mojefedora.cz/cargo-spravce-projektu-a-balicku-pro-programovaci-jazyk-rust/
  2. Network Communication and Serialization in Rust
    https://www.safaribookson­line.com/blog/2014/01/28/net­work-communication-serialization-rust/
  3. Crate bincode
    http://tyoverby.com/binco­de/bincode/index.html
  4. Struct std::fs::File
    https://doc.rust-lang.org/std/fs/struct.File.html
  5. Trait std::io::Seek
    https://doc.rust-lang.org/std/io/trait.Seek.html
  6. Trait std::io::Read
    https://doc.rust-lang.org/std/io/trait.Read.html
  7. Trait std::io::Write
    https://doc.rust-lang.org/std/io/trait.Write.html
  8. Trait std::io::BufRead
    https://doc.rust-lang.org/std/io/trait.BufRead.html
  9. Module std::io::prelude
    https://doc.rust-lang.org/std/io/prelude/index.html
  10. std::net::IpAddr
    https://doc.rust-lang.org/std/net/enum.IpAddr.html
  11. std::net::Ipv4Addr
    https://doc.rust-lang.org/std/net/struct.Ipv4Addr.html
  12. std::net::Ipv6Addr
    https://doc.rust-lang.org/std/net/struct.Ipv6Addr.html
  13. TcpListener
    https://doc.rust-lang.org/std/net/struct.TcpLis­tener.html
  14. TcpStream
    https://doc.rust-lang.org/std/net/struct.TcpStream.html
  15. Binary heap (Wikipedia)
    https://en.wikipedia.org/wi­ki/Binary_heap
  16. Binární halda (Wikipedia)
    https://cs.wikipedia.org/wi­ki/Bin%C3%A1rn%C3%AD_halda
  17. Halda (datová struktura)
    https://cs.wikipedia.org/wi­ki/Halda_%28datov%C3%A1_struk­tura%29
  18. Struct std::collections::HashSet
    https://doc.rust-lang.org/std/collections/struc­t.HashSet.html
  19. Struct std::collections::BTreeSet
    https://doc.rust-lang.org/std/collections/struc­t.BTreeSet.html
  20. Struct std::collections::BinaryHeap
    https://doc.rust-lang.org/std/collections/struc­t.BinaryHeap.html
  21. Set (abstract data type)
    https://en.wikipedia.org/wi­ki/Set_%28abstract_data_ty­pe%29#Language_support
  22. Associative array
    https://en.wikipedia.org/wi­ki/Associative_array
  23. Hash Table
    https://en.wikipedia.org/wi­ki/Hash_table
  24. B-tree
    https://en.wikipedia.org/wiki/B-tree
  25. Pedro Celis: Robin Hood Hashing (naskenované PDF!)
    https://cs.uwaterloo.ca/re­search/tr/1986/CS-86–14.pdf
  26. Robin Hood hashing
    http://codecapsule.com/2013/11/11/ro­bin-hood-hashing/
  27. Robin Hood hashing: backward shift deletion
    http://codecapsule.com/2013/11/17/ro­bin-hood-hashing-backward-shift-deletion/
  28. Module std::collections
    https://doc.rust-lang.org/std/collections/
  29. Module std::vec
    https://doc.rust-lang.org/nightly/std/vec/index.html
  30. Struct std::collections::VecDeque
    https://doc.rust-lang.org/std/collections/struc­t.VecDeque.html
  31. Struct std::collections::LinkedList
    https://doc.rust-lang.org/std/collections/struc­t.LinkedList.html
  32. Module std::fmt
    https://doc.rust-lang.org/std/fmt/
  33. Macro std::println
    https://doc.rust-lang.org/std/macro.println.html
  34. Enum std::result::Result
    https://doc.rust-lang.org/std/result/enum.Result.html
  35. Module std::result
    https://doc.rust-lang.org/std/result/
  36. Result
    http://rustbyexample.com/std/re­sult.html
  37. Rust stdlib: Option
    https://doc.rust-lang.org/std/option/enum.Option.html
  38. Module std::option
    https://doc.rust-lang.org/std/option/index.html
  39. Rust by example: option
    http://rustbyexample.com/std/op­tion.html
  40. Rust by example: if-let
    http://rustbyexample.com/flow_con­trol/if_let.html
  41. Rust by example: while let
    http://rustbyexample.com/flow_con­trol/while_let.html
  42. Rust by example: Option<i32>
    http://rustbyexample.com/std/op­tion.html
  43. An Overview of Macros in Rust
    http://words.steveklabnik.com/an-overview-of-macros-in-rust
  44. A Practical Intro to Macros in Rust 1.0
    https://danielkeep.github.io/practical-intro-to-macros.html
  45. The Rust Programming Language: macros
    https://doc.rust-lang.org/beta/book/macros.html
  46. Rust by example: 15 macro_rules!
    http://rustbyexample.com/macros.html
  47. Primitive Type isize
    https://doc.rust-lang.org/nightly/std/primi­tive.isize.html
  48. Primitive Type usize
    https://doc.rust-lang.org/nightly/std/primi­tive.usize.html
  49. Primitive Type array
    https://doc.rust-lang.org/nightly/std/primi­tive.array.html
  50. Module std::slice
    https://doc.rust-lang.org/nightly/std/slice/
  51. Rust by Example: 2.3 Arrays and Slices
    http://rustbyexample.com/pri­mitives/array.html
  52. What is the difference between Slice and Array (stackoverflow)
    http://stackoverflow.com/qu­estions/30794235/what-is-the-difference-between-slice-and-array
  53. Learning Rust With Entirely Too Many Linked Lists
    http://cglab.ca/~abeinges/blah/too-many-lists/book/
  54. Testcase: linked list
    http://rustbyexample.com/cus­tom_types/enum/testcase_lin­ked_list.html
  55. Operators and Overloading
    https://doc.rust-lang.org/book/operators-and-overloading.html
  56. Module std::ops
    https://doc.rust-lang.org/std/ops/index.html
  57. Module std::cmp
    https://doc.rust-lang.org/std/cmp/index.html
  58. Trait std::ops::Add
    https://doc.rust-lang.org/stable/std/ops/trait.Add.html
  59. Trait std::ops::AddAssign
    https://doc.rust-lang.org/std/ops/trait.AddAssign.html
  60. Trait std::ops::Drop
    https://doc.rust-lang.org/std/ops/trait.Drop.html
  61. Trait std::cmp::Eq
    https://doc.rust-lang.org/std/cmp/trait.Eq.html
  62. Struct std::boxed::Box
    https://doc.rust-lang.org/std/boxed/struct.Box.html
  63. Explore the ownership system in Rust
    https://nercury.github.io/rus­t/guide/2015/01/19/ownership­.html
  64. Rust's ownership and move semantic
    http://www.slideshare.net/sa­neyuki/rusts-ownership-and-move-semantics
  65. Trait std::marker::Copy
    https://doc.rust-lang.org/stable/std/marker/tra­it.Copy.html
  66. Trait std::clone::Clone
    https://doc.rust-lang.org/stable/std/clone/tra­it.Clone.html
  67. The Stack and the Heap
    https://doc.rust-lang.org/book/the-stack-and-the-heap.html
  68. Rust Compare: Pointers & References
    http://www.rust-compare.com/site/pointers.html
  69. Rust Compare: Parameters
    http://www.rust-compare.com/site/params.html
  70. Why does this compile? Automatic dereferencing?
    https://users.rust-lang.org/t/why-does-this-compile-automatic-dereferencing/2183
  71. Understanding Pointers, Ownership, and Lifetimes in Rust
    http://koerbitz.me/posts/Understanding-Pointers-Ownership-and-Lifetimes-in-Rust.html
  72. Rust lang series episode #25 — pointers (#rust-series)
    https://steemit.com/rust-series/@jimmco/rust-lang-series-episode-25-pointers-rust-series
  73. Rust – home page
    https://www.rust-lang.org/en-US/
  74. Rust – Frequently Asked Questions
    https://www.rust-lang.org/en-US/faq.html
  75. Destructuring and Pattern Matching
    https://pzol.github.io/get­ting_rusty/posts/20140417_des­tructuring_in_rust/
  76. The Rust Programming Language
    https://doc.rust-lang.org/book/
  77. Rust (programming language)
    https://en.wikipedia.org/wi­ki/Rust_%28programming_lan­guage%29
  78. Go – home page
    https://golang.org/
  79. Stack Overflow – Most Loved, Dreaded, and Wanted language
    https://stackoverflow.com/re­search/developer-survey-2016#technology-most-loved-dreaded-and-wanted
  80. 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/
  81. Rust vs Go: My experience
    https://www.reddit.com/r/go­lang/comments/21m6jq/rust_vs_go_my_ex­perience/
  82. Friends of Rust (Organizations running Rust in production)
    https://www.rust-lang.org/en-US/friends.html
  83. Rust programs versus C++ g++
    https://benchmarksgame.ali­oth.debian.org/u64q/compa­re.php?lang=rust&lang2=gpp
  84. Další benchmarky (nejedná se o reálné příklady „ze života“)
    https://github.com/kostya/benchmarks
  85. Go na Redditu
    https://www.reddit.com/r/golang/
  86. Rust vs. Go
    http://vschart.com/compare/rust/vs/go-language
  87. Abstraction without overhead: traits in Rust
    https://blog.rust-lang.org/2015/05/11/traits.html
  88. Method Syntax
    https://doc.rust-lang.org/book/method-syntax.html
  89. Traits in Rust
    https://doc.rust-lang.org/book/traits.html
  90. Functional Programming in Rust – Part 1 : Function Abstraction
    http://blog.madhukaraphatak­.com/functional-programming-in-rust-part-1/
  91. 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
  92. Chytré ukazatele (moderní verze jazyka C++) [MSDN]
    https://msdn.microsoft.com/cs-cz/library/hh279674.aspx
  93. UTF-8 Everywhere
    http://utf8everywhere.org/
  94. Rust by Example
    http://rustbyexample.com/
  95. Rust oficiálně ve Fedoře
    https://mojefedora.cz/rust-oficialne-ve-fedore/
  96. Resource acquisition is initialization
    https://en.wikipedia.org/wi­ki/Resource_acquisition_is_i­nitialization
  97. TIOBE index (October 2016)
    http://www.tiobe.com/tiobe-index/
  98. Porovnání Go, D a Rustu na OpenHubu:
    https://www.openhub.net/lan­guages/compare?language_na­me[]=-1&language_name[]=-1&language_name[]=dmd&lan­guage_name[]=golang&langu­age_name[]=rust&language_na­me[]=-1&measure=commits
  99. String Types in Rust
    http://www.suspectsemantic­s.com/blog/2016/03/27/str­ing-types-in-rust/
  100. Trait (computer programming)
    https://en.wikipedia.org/wi­ki/Trait_%28computer_program­ming%29
  101. Type inference
    https://en.wikipedia.org/wi­ki/Type_inference
Našli jste v článku chybu?