Obsah
2. Vypsání adresy ukazatele na straně Rustu i céčka
3. Předání adresy pole do funkce v nativní knihovně
4. Druhý demonstrační příklad – výpočet sumy prvků pole
5. Nedetekovaný problém při předání datové struktury přes ukazatel
6. Použití nástroje Cargo při práci s projekty používajícími nativní knihovny
7. Nastavení projektu spravovaného nástrojem Cargo
9. Změny, které je nutné provést ve zdrojových kódech
10. Překlad a spuštění projektu
11. Zjednodušení skriptu pro překlad s použitím pluginu gcc
13. Repositář s demonstračními příklady
1. Kódování znaků v řetězcích
Již v úvodních částech seriálu o programovacím jazyku Rust jsme si řekli, že textové řetězce jsou interně ukládány s využitím kódování UTF-8, což sice může znít překvapivě, ovšem přináší to i některé výhody. Autoři tohoto jazyka správně poukazují na to, že v současnosti prakticky všechny webové služby, XML soubory, JSON soubory, velká část HTML stránek atd. stejně již kódování UTF-8 standardně používají, takže nemá význam neustále provádět konverzi mezi tímto kódováním a například UCS-4 (UTF-32). Navíc je při zpracování rozsáhlých XML souborů formát UTF-8 výhodnější z hlediska spotřeby operační paměti. Největší nevýhodou použití UTF-8 je nemožnost získat a vrátit n-tý znak v řetězci v konstantním čase (a na některých architekturách též obtížná práce s jednotlivými bajty). Pokud by se operace pro získání n-tého znaku prováděla velmi často, lze samozřejmě použít vhodný objekt, který například „obaluje“ pole čtyřbajtových širokých znaků reprezentovaných v UCS-4/UTF-32.
V demonstračním příkladu popsaném v navazující kapitole bude použit řetězec obsahující znaky s českými akcenty, takže z výpisu bude patrné, jakým způsobem jsou tyto znaky reprezentovány i na straně céčkového programu (resp. na straně nativní knihovny).
2. Vypsání adresy ukazatele na straně Rustu i céčka
V předchozí části seriálu o Rustu jsme si řekli, že řetězce se do volaných nativních knihoven předávají přes ukazatel. To mj. znamená, že vlastní hodnota uložená v proměnné typu ukazatel na straně Rustu bude shodná s adresou uloženou v parametru typu char * či const char * na straně céčka. Toto tvrzení si můžeme snadno ověřit, protože adresu samozřejmě můžeme vypsat jak v céčkovém programu, tak i v té části, která je naprogramovaná v Rustu. Céčková část aplikace bude vypadat například takto (jak je patrné, stále budeme volat funkci vracející délku řetězce v bajtech):
#include <stdint.h> #include <stdio.h> int32_t string_length(const char *str) { printf("C side pointer: %p\n", str); int32_t len = 0; for (; *str; str++, len++) ; return len; }
V té části aplikace, která je naprogramovaná v Rustu, získáme ukazatel s využitím metody CString.as_ptr() a ihned poté můžeme vypsat jeho hodnotu (tedy adresu):
fn test(string :&'static str) { match CString::new(string) { Ok(string) => { let pointer = string.as_ptr(); println!("Rust side pointer: {:p}", pointer); unsafe { println!("string length = {}", string_length(pointer)); } } Err(error) => { println!("CString::new() error: {:?}", error); } } }
Pro zjištění, jak přesně se chovají znaky s akcenty v řetězcích, budeme funkci test() volat takto:
test("Hello world!"); test(""); test("ěščřžýáíé");
Celá rustovská část zdrojového kódu vypadá takto:
use std::ffi::CString; use std::os::raw::c_char; #[link(name = "ffi4")] extern { fn string_length(str: *const c_char) -> i32; } fn test(string :&'static str) { match CString::new(string) { Ok(string) => { let pointer = string.as_ptr(); println!("Rust side pointer: {:p}", pointer); unsafe { println!("string length = {}", string_length(pointer)); } } Err(error) => { println!("CString::new() error: {:?}", error); } } } fn main() { test("Hello world!"); test(""); test("ěščřžýáíé"); }
Pro překlad obou částí aplikace a pro její následné spuštění opět použijeme pomocný soubor Makefile (ale níže si vysvětlíme lepší způsob spočívající ve využití projektu Cargo):
CC=gcc CFLAGS=-Wall -ansi -fPIC RUSTC=rustc APP=ffi4 PROGNAME=$(APP) LIBNAME=lib$(APP).so RUST_SRC=$(APP).rs LIB_SRC=$(APP).c OBJ_FILE=$(APP).o # Vychozi pravidlo pro vytvoreni vysledne spustitelne aplikace. all: $(LIBNAME) $(PROGNAME) # Pravidlo pro slinkovani vsech objektovych souboru a vytvoreni # vysledne spustitelne aplikace. $(PROGNAME): $(RUST_SRC) $(LIBNAME) $(RUSTC) -L . $< # Pravidlo pro preklad knihovny $(LIBNAME): $(OBJ_FILE) $(CC) -shared -Wl,-soname,$(LIBNAME) -o $(LIBNAME) $< %.o: %.c $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ clean: rm -f *.o rm -f *.so rm -f $(PROGNAME) run: export LD_LIBRARY_PATH=.;./$(APP)
Po překladu a spuštění tohoto příkladu můžeme získat například následující výstup. Konkrétní adresy řetězce se samozřejmě mohou odlišovat, obě však budou shodné (na straně Rustu i na straně nativní knihovny), protože se skutečně předává adresa řetězce a nikoli jeho obsah:
Rust side pointer: 0x2b8e67a26000 C side pointer: 0x2b8e67a26000 string length = 12 Rust side pointer: 0x2b8e67a1d020 C side pointer: 0x2b8e67a1d020 string length = 0 Rust side pointer: 0x2b8e67a1c0a0 C side pointer: 0x2b8e67a1c0a0 string length = 18
Povšimněte si, že vypočtená délka řetězce „ěščřžýáíé“ je osmnáct bajtů, zatímco samotný řetězec má jen devět znaků. Je to samozřejmě způsobeno použitým kódováním znaků. Malou úpravou příkladu lze kódy znaků snadno získat:
#include <stdio.h> int32_t string_length(const char *str) { printf("C side pointer: %p\n", str); int32_t len = 0; for (; *str; str++, len++) printf("%02x ", (unsigned char)*str); ; return len; }
S tímto výsledkem:
Rust side pointer: 0x2b3d2be26000 C side pointer: 0x2b3d2be26000 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 string length = 12 Rust side pointer: 0x2b3d2be1d020 C side pointer: 0x2b3d2be1d020 string length = 0 Rust side pointer: 0x2b3d2be1c0a0 C side pointer: 0x2b3d2be1c0a0 c4 9b c5 a1 c4 8d c5 99 c5 be c3 bd c3 a1 c3 ad c3 a9 string length = 18
Poznámka: ve skutečnosti není příliš dobré používat standardní výstup společně na straně C a Rustu, protože se kvůli bufferování výstupu mohou řádky promíchat.
3. Předání adresy pole do funkce v nativní knihovně
Nyní si ukažme, jakým způsobem se do nativní funkce předá pole, konkrétně pole prvků typu f32. Již na začátku si však musíme uvědomit, že mezi poli v Rustu a céčku existuje mnoho rozdílů – pole v Rustu nejvíce odpovídají céčkovým polím s délkou známou překladači (vytvořenými přes []), zatímco dynamicky vytvořeným céčkovým polím ideově odpovídají řezy (slices), u nichž není délka známá v době překladu.
V předchozí části tohoto seriálu jsme si uvedli tabulku s převodem základních (primitivních) datových typů jazyka Rust a odpovídajících typů v céčku. Nyní si do této tabulky přidáme i informace o datových typech určených pro reprezentaci číselných hodnot reprezentovaných v systému plovoucí řádové čárky (floating point). V Rustu se jedná o typy f32 a f64, na straně céčka pak o typy float a double:
Typ v C | Typ v Rustu | Význam |
---|---|---|
int8_t | i8 | celé číslo se znaménkem (signed) o šířce 8 bitů |
int16_t | i16 | celé číslo se znaménkem (signed) o šířce 16 bitů |
int32_t | i32 | celé číslo se znaménkem (signed) o šířce 32 bitů |
int64_t | i64 | celé číslo se znaménkem (signed) o šířce 64 bitů |
uint8_t | u8 | celé číslo bez znaménka (unsigned) o šířce 8 bitů |
uint16_t | u16 | celé číslo bez znaménka (unsigned) o šířce 16 bitů |
uint32_t | u32 | celé číslo bez znaménka (unsigned) o šířce 32 bitů |
uint64_t | u64 | celé číslo bez znaménka (unsigned) o šířce 64 bitů |
float | f32 | numerická hodnota s plovoucí řádovou čárkou podle IEEE 754 s jednoduchou přesností |
double | f64 | numerická hodnota s plovoucí řádovou čárkou podle IEEE 754 s dvojitou přesností |
Pole prvků typu f32 budeme předávat, podobně jako tomu bylo u řetězců, přes ukazatel. Jelikož se bude předávat pouze „datová část“ pole bez dalších atributů, musíme všechny další informace nativní funkci předávat explicitně. Jedinou další požadovanou informací bude délka pole (u řetězců byla tato délka stanovena nepřímo vzdáleností bajtu s kódem nula od začátku řetězce). Námi vytvořená nativní funkce spočítá součet všech prvků pole; funkci budeme explicitně předávat délku i ukazatel na první prvek v poli:
#include <stdint.h> #include <stdio.h> float sum(uint32_t len, const float *array) { float s = 0.0f; int i; for (i=0; i < len; i++) { s += array[i]; } return s; }
Poznámka: ve skutečnosti není vložení hlavičkového souboru stdio.h v tomto zdrojovém kódu nutné, pokud si ovšem opět nebudete chtít vypsat konkrétní předané parametry.
4. Druhý demonstrační příklad – výpočet sumy prvků pole
V rustovské části aplikace je nejprve nutné správně deklarovat hlavičku volané nativní funkce. Použijeme přitom typy std::os::raw::c_float a std::os::raw::c_uint:
#[link(name = "ffi5")] extern { fn sum(len: c_uint, arr: *const c_float) -> c_float; }
Dále pro dané pole získáme ukazatel s využitím metody as_ptr(), vypočteme délku pole metodou len() a tyto údaje předáme nativní funkci. Celá část, v níž se pracuje s ukazatelem, bude vložena do bloku unsafe:
let array : [f32; 5] = [1.0, 2.0, 3.0, 4.0, 5.0]; unsafe { let pointer = array.as_ptr(); let len : u32 = array.len() as u32; let s = sum(len, pointer); println!("sum = {}", s); }
Celý zdrojový kód může vypadat následovně:
use std::os::raw::c_float; use std::os::raw::c_uint; #[link(name = "ffi5")] extern { fn sum(len: c_uint, arr: *const c_float) -> c_float; } fn main() { let array : [f32; 5] = [1.0, 2.0, 3.0, 4.0, 5.0]; unsafe { let pointer = array.as_ptr(); let len : u32 = array.len() as u32; let s = sum(len, pointer); println!("sum = {}", s); } }
Po překladu a spuštění dostaneme lakonickou zprávu o součtu prvků pole:
sum = 15
5. Nedetekovaný problém při předání datové struktury přes ukazatel
Pokud se pokusíme přeložit tento program:
fn main() { let array : [f32; 5] = [1.0, 2.0, 3.0, 4.0, 5.0]; for i in 1..6 { array[i] = 0.0; } }
vypíše překladač chybové hlášení, protože se pokoušíme o zápis do pole, které je neměnitelné (immutable):
error: cannot assign to immutable indexed content `array[..]` --> ffi5.rs:12:9 | 12 | array[i] = 0.0; | ^^^^^^^^^^^^^^ error: aborting due to previous error
Ve skutečnosti se ovšem jedná „pouze“ o omezení kladená překladačem. Pokud například upravíme nativní část aplikace tak, aby funkce kromě součtu prvků ještě prvky pole postupně vynulovala, nebude to překladačem zachyceno (ostatně do funkce se předává pouhý ukazatel):
#include <stdint.h> #include <stdio.h> float sum(uint32_t len, float *array) { float s = 0.0f; int i; for (i=0; i < len; i++) { s += array[i]; array[i] = 0.0f; } return s; }
Rustovská část aplikace bude vypadat shodně s předchozím demonstračním příkladem:
use std::os::raw::c_float; use std::os::raw::c_uint; #[link(name = "ffi6")] extern { fn sum(len: c_uint, arr: *const c_float) -> c_float; } fn main() { let array : [f32; 5] = [1.0, 2.0, 3.0, 4.0, 5.0]; println!("array: {:?}", array); unsafe { let pointer = array.as_ptr(); let len : u32 = array.len() as u32; let s = sum(len, pointer); println!("sum = {}", s); } println!("array: {:?}", array); }
Pokud tento program spustíte, vypíše s velkou pravděpodobností následující výsledek:
array: [1, 2, 3, 4, 5] sum = 15 array: [0, 0, 0, 0, 0]
Povšimněte si, že nativní funkce změnila hodnoty zapsané v poli, i když je pole zdánlivě neměnitelné! Navíc může nastat další problém – prvky pole mohou být uloženy v kódovém segmentu a při pokusu o jeho modifikaci dojde k pádu programu (což by bylo zcela korektní chování). Jinými slovy – při použití FFI ztrácíme poměrně velkou část výhod Rustu spočívající v přísných kontrolách prováděných překladačem.
6. Použití nástroje Cargo při práci s projekty používajícími nativní knihovny
Použití souborů Makefile nebo dokonce shellových skriptů určených pro překlad a slinkování aplikace složené z céčkové části a rustovské části je poměrně nešikovné a v praxi se s ním příliš často nesetkáme. Mnohem častěji se používá nástroj Cargo, který mj. podporuje i tvorbu vlastních build skriptů.
Zkusme si tedy nejprve vytvořit prázdný projekt spravovaný systémem Cargo. Projekt bude sloužit k vytvoření spustitelné binární aplikace a proto musíme použít přepínač –bin:
cargo new --bin ffi1
Po spuštění předchozího příkazu získáme následující adresářovou strukturu projektu:
. ├── Cargo.toml └── src └── main.rs
Do projektu přesuneme soubor ffi1.c (s funkcí pro součet dvou celých čísel) a umístíme ho do podadresáře src. Navíc vytvoříme (například pomocí touch) soubor nazvaný build.rs, který bude umístěn ve stejném adresáři, v jakém se nachází projektový soubor Cargo.toml. Výsledek by měl vypadat následovně:
. ├── build.rs ├── Cargo.toml └── src ├── ffi1.c └── main.rs
7. Nastavení projektu spravovaného nástrojem Cargo
Projektový soubor Cargo.toml v mém případě vypadal následovně (ve vašem případě se samozřejmě bude lišit řádek authors, ovšem to není v tuto chvíli příliš důležité):
[package] name = "ffi_project1" version = "0.1.0" authors = ["Pavel Tisnovsky <ptisnovs@redhat.com>"] [dependencies]
Do projektového souboru musíme přidat další direktivu, která říká, že se před překladem a před linkováním projektu má spustit i takzvaný build skript (ve skutečnosti to skript není, protože se jedná o zdrojový kód psaný v Rustu a překládaný do strojového kódu). Přidaný řádek je zvýrazněn:
[package] name = "ffi_project1" version = "0.1.0" authors = ["Pavel Tisnovsky <ptisnovs@redhat.com>"] build = "build.rs" [dependencies]
Tento řádek má vliv jen na průběh překladu, nikoli na spouštění samotné aplikace.
8. Soubor build.rs
Samotný soubor build.rs vlastně představuje zdrojový kód běžného programu napsaného v Rustu, který ovšem přes svůj standardní výstup může předávat parametry překladači Rustu (typicky cesty ke knihovnám atd.). Úkolem tohoto souboru bude provést tři kroky, které jsme předtím implementovali přes soubor Makefile nebo jednoduchými shellovými skripty:
gcc -c src/ffi1.c -fPIC -o ffi1.o ar rcsu libffi1.a ffi1.o
Připomeňme si, že z Rustu je možné relativně snadno spouštět nové procesy a předávat jim parametry. Právě tuto funkci nyní využijeme pro napodobení předchozího shell skriptu. Nejprve zjistíme jméno adresáře, kam se ukládají výsledné binární soubory (tento adresář může být odlišný podle typu překladu):
let out_dir = env::var("OUT_DIR").unwrap();
Následně spustíme překladač gcc a utilitu pro práci se statickými knihovnami ar, samozřejmě s předáním správných parametrů:
// preklad Command::new("gcc").args(&["src/ffi1.c", "-c", "-fPIC", "-o"]) .arg(&format!("{}/ffi1.o", out_dir)) .status().unwrap(); // import objektoveho souboru do staticke knihovny Command::new("ar").args(&["crus", "libffi1.a", "ffi1.o"]) .current_dir(&Path::new(&out_dir)) .status().unwrap();
Posledním krokem je předání parametrů překladači a linkeru. To se provádí poměrně elegantním způsobem přes standardní výstup – každý řádek vypisovaný aplikací build začínající textem „cargo:“ je zpracován, rozpoznán, převeden na správný přepínač a předán překladači a linkeru Rustu. Většina možných voleb je popsána na stránce http://doc.crates.io/build-script.html#outputs-of-the-build-script, nás však dnes bude zajímat prakticky jen volba rustc-link-lib=[KIND=]NAME, která je transformována do přepínače -l, s nímž jsme se již seznámili (za KIND lze doplnit static nebo dynlib) a volba ustc-link-search= transformovaná do přepínače -L (cesta ke knihovnám):
// predame prekladaci parametry println!("cargo:rustc-link-search=native={}", out_dir); println!("cargo:rustc-link-lib=static=ffi1");
Úplná podoba souboru build.rs je tedy následující:
// build.rs use std::process::Command; use std::env; use std::path::Path; fn main() { let out_dir = env::var("OUT_DIR").unwrap(); // preklad Command::new("gcc").args(&["src/ffi1.c", "-c", "-fPIC", "-o"]) .arg(&format!("{}/ffi1.o", out_dir)) .status().unwrap(); // import objektoveho souboru do staticke knihovny Command::new("ar").args(&["crus", "libffi1.a", "ffi1.o"]) .current_dir(&Path::new(&out_dir)) .status().unwrap(); // predame prekladaci parametry println!("cargo:rustc-link-search=native={}", out_dir); println!("cargo:rustc-link-lib=static=ffi1"); }
9. Změny, které je nutné provést ve zdrojových kódech
Další změnu je zapotřebí provést ve zdrojových kódech. Netýká se to ovšem céčkové části, která zůstává stále stejná (ostatně zde není k žádné změně důvod):
#include <stdint.h> int32_t add(int32_t x, int32_t y) { return x+y; }
V rustovské části aplikace je nutné odstranit řádek:
#[link(name = "ffi1", kind= "static")]
Pokud by tento řádek ve zdrojovém kódu zůstal, došlo by k pokusu o slinkování knihovny dvakrát, což by vedlo k chybě při kompilaci projektu:
$ cargo build Compiling ffi_project1 v0.1.0 (file:///home/tester/ffi_project1) error: linking with `cc` failed: exit code: 1 | = note: "cc" "-Wl,--as-needed" ...vynecháno... = note: /home/tester/ffi_project1/target/debug/build/ffi_project1-14178b3c9909eb85/out/libffi1.a(ffi1.o): In function `add': ffi1.c:(.text+0x0): multiple definition of `add' /home/tester/ffi_project1/target/debug/build/ffi_project1-14178b3c9909eb85/out/libffi1.a(ffi1.o):ffi1.c:(.text+0x0): first defined here collect2: error: ld returned 1 exit status error: aborting due to previous error error: Could not compile `ffi_project1`. To learn more, run the command again with --verbose.
Nová podoba rustovské části se tedy ve skutečnosti nepatrně zjednodušila:
extern { fn add(x:i32, y:i32) -> i32; } fn main() { let x:i32 = 1; let y:i32 = 2; let z = unsafe { add(x, y) }; println!("{} + {} = {}", x, y, z); }
10. Překlad a spuštění projektu
Po výše provedených úpravách je překlad a slinkování projektu velmi snadné a zajistí ho příkaz:
cargo build Compiling ffi_project1 v0.1.0 (file:///home/tester/ffi_project1) Finished debug [unoptimized + debuginfo] target(s) in 1.1 secs
Spuštění se provede stejně snadno:
cargo run Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs Running `target/debug/ffi_project1` 1 + 2 = 3
Překlad do finální podoby (s optimalizacemi atd.):
cargo build --release Compiling ffi_project1 v0.1.0 (file:///home/tester/ffi_project1) Finished release [optimized] target(s) in 1.10 secs
11. Zjednodušení skriptu pro překlad s použitím pluginu gcc
Build skript z předchozího projektu explicitně volal překladač céčka a utilitu ar, což je ovšem poměrně špatný přístup k řešení problému (prakticky stejně špatný jako naše soubory Makefile), a to z toho důvodu, že zbytečně explicitně určujeme, jaký překladač se má volat, jaké se mu mají předávat parametry atd. To ovšem vede k tvorbě potenciálně nepřenositelných zdrojových kódů. Mnohem lepší je použít modul (v systému Cargo se jim říká crate) http://alexcrichton.com/gcc-rs/gcc/index.html. Jméno tohoto modulu je mírně zavádějící, protože může podporovat i další typy překladačů. Vzhledem k tomu, že je tento modul používán pro sestavení aplikace, přidává se do sekce [build-dependencies] a nikoli [dependencies]:
[build-dependencies] gcc = "0.3"
Do projektového souboru Cargo.toml je zapotřebí přidat dva zvýrazněné řádky:
name = "ffi_project2" version = "0.1.0" authors = ["Pavel Tisnovsky <ptisnovs@redhat.com>"] build = "build.rs" [dependencies] [build-dependencies] gcc = "0.3"
12. Upravený soubor build.rs
Musíme upravit i soubor build.rs, který se nyní zjednoduší, protože bude ve funkci main obsahovat jen jediný příkaz – volání funkce gcc::compile_library(). Tato funkce již zajistí vše ostatní, tedy i předání parametrů překladači a linkeru Rustu:
// balicek gcc zajisti preklad a vytvoreni knihovny za nas extern crate gcc; fn main() { // preklad a vytvoreni knihovny gcc::compile_library("libffi2.a", &["src/ffi2.c"]); }
Překlad se nemění:
cargo build Compiling gcc v0.3.50 Compiling ffi_project2 v0.1.0 (file:///home/tester/temp/presentations/rust/ffi/ffi_project2) Finished debug [unoptimized + debuginfo] target(s) in 3.42 secs
Spuštění bude taktéž stejné:
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs Running `target/debug/ffi_project2` 1 + 2 = 3
13. Repositář s demonstračními příklady
Všechny dnes popisované demonstrační příklady a podpůrné skripty 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. Demonstrační příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý repositář (ovšem u projektů je lepší mít celý repositář, abyste nemuseli pracně stahovat všechny potřebné soubory):
14. Odkazy na Internetu
- Build Script Support
http://doc.crates.io/build-script.html - Creating a shared and static library with the gnu compiler [gcc]
http://www.adp-gmbh.ch/cpp/gcc/create_lib.html - FFI: Foreign Function Interface
https://doc.rust-lang.org/book/ffi.html - Primitive Type pointer
https://doc.rust-lang.org/std/primitive.pointer.html - 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