Obsah
1. Programovací jazyk Rust: rozhraní mezi Rustem a Pythonem
2. Překlad zdrojového kódu Rustu do dynamické knihovny
3. Vytvoření projektu pro překlad zdrojového kódu do dynamické knihovny
4. Zdrojový kód funkce, která se přeloží do dynamické knihovny
5. Zdrojový kód Pythonovského skriptu, který má dynamickou knihovnu využít
6. Překlad projektu a pokus zavolání funkce z dynamické knihovny
7. Name mangling a jeho důsledky
8. Zákaz name manglingu při překladu
9. Načtení dynamické knihovny bez uvedení cesty ve zdrojovém kódu
10. Předávání struktur mezi Rustem a Pythonem
12. Skript psaný v Pythonu, který volá funkce z dynamické knihovny
13. Spuštění skriptu, který zavolá funkci pro součet komplexních čísel
14. Předávání struktur odkazem
15. Repositář s demonstračními příklady
1. Programovací jazyk Rust: rozhraní mezi Rustem a Pythonem
V předchozích třech částech [1] [2] [3] seriálu o programovacím jazyku Rust jsme se zabývali problematikou volání nativních funkcí (psaných většinou v jazyku C, i když to samozřejmě není nutná podmínka) z Rustu, a to konkrétně s využitím technologie FFI (Foreign Function Interface). Dnes bude programovací jazyk Rust vystupovat v opačné roli – programy psané v Rustu již nebudou „konzumenty“ cizích nativních knihoven ale naopak bude Rust sloužit pro vytvoření dynamických knihoven, jejichž funkce budeme volat z dalšího programovacího jazyka, konkrétně z Pythonu. Propojíme tak Rust s dnes pravděpodobně nejpopulárnějším vysokoúrovňovým programovacím jazykem současnosti.
2. Překlad zdrojového kódu Rustu do dynamické knihovny
Vzhledem k tomu, že překladač programovacího jazyka Rust kompiluje zdrojové kódy do běžného nativního objektového kódu zpracovávaného linkerem, je zřejmé, že i v Rustu je možné vytvářet běžné statické i dynamické knihovny. Musíme ovšem zajistit, aby překladač žádným způsobem nemodifikoval jména funkcí, protože rustovský překladač implicitně provádí takzvané name mangling, s čímž se většina programátorů pravděpodobně setkala spíše v souvislosti s programovacím jazykem C++. Pokud tedy v Rustu name mangling zakážeme a nastavíme správce projektů Cargo takovým způsobem, aby po překladu vytvořil běžnou dynamickou knihovnu, bude možné tuto knihovnu bez větších problémů použít v Pythonu (a samozřejmě i v jakémkoli jiném programovacím jazyku). Na straně Pythonu lze pro volání nativních funkcí použít například standardní modul ctypes nebo sice méně známý, ale o to povedenější modul CFFI. Dnes použité příklady budou pro jednoduchost používat modul ctypes, který ve svém systému již pravděpodobně máte nainstalovaný.
3. Vytvoření projektu pro překlad zdrojového kódu do dynamické knihovny
Nejprve si ukážeme, jak se s využitím nástroje Cargo vytvoří a následně nakonfiguruje projekt, který bude sloužit pro vytvoření dynamické knihovny s přeloženými funkcemi Rustu. Nový projekt vytvoříme nám již známým příkazem cargo new, kterému nyní ovšem nepředáme přepínač –bin, protože výsledkem našeho projektu nemá být spustitelný program:
cargo new test1
Tento příkaz by měl vytvořit nový adresář pojmenovaný test1, v němž bude tato struktura souborů a podadresářů:
. ├── Cargo.toml └── src └── lib.rs
Nyní je zapotřebí upravit projektový soubor Cargo.toml, konkrétně do něj přidat dva řádky, které jsou na dalším výpisu zvýrazněny. Těmito řádky se specifikuje – zjednodušeně řečeno – typ výsledku překladu:
[package] name = "test1" version = "0.1.0" authors = ["Pavel Tisnovsky <ptisnovs@redhat.com>"] [lib] crate-type = ["dylib"] [dependencies]
Specifikujeme, že výsledkem překladu bude dynamická knihovna libtest1.so nebo test1.dll.
4. Zdrojový kód funkce, která se přeloží do dynamické knihovny
Původní obsah souboru src/lib.rc vymažeme a přepíšeme následujícím obsahem:
pub extern fn add_integers(x: i32, y: i32) -> i32 { x + y }
Jedná se o nekomplikovanou funkci s jediným výrazem, jehož výsledná hodnota je současně návratovou hodnotou celé funkce (povšimněte si chybějícího středníku na konci výrazu).
5. Zdrojový kód Pythonovského skriptu, který má dynamickou knihovnu využít
Nyní je nutné vytvořit druhou část projektu naprogramovanou v Pythonu. Tato část bude jednoduchá, protože sestává z jediného souboru test.py uloženého v adresáři s projektem (opět pro jednoduchost, v reálném světě je situace poněkud odlišná):
#!/usr/bin/env python3 import ctypes testlib1 = ctypes.CDLL("target/debug/libtest1.so") result = testlib1.add_integers(1, 2) print("1 + 2 = {}".format(result)) result = testlib1.add_integers(1.5, 2) print("1.5 + 2 = {}".format(result))
Povšimněte si, že nejdříve otevřeme dynamickou knihovnu, k níž je uvedena plná cesta. To není obvyklý způsob, neboť v praxi je lepší se spolehnout na proměnnou LD_LIBRARY_PATH, což si ukážeme v dalších demonstračních projektech.
Výsledná struktura celého projektu by nyní měla vypadat následovně:
. ├── Cargo.toml ├── src │ └── lib.rs └── test.py
6. Překlad projektu a pokus zavolání funkce z dynamické knihovny
Překlad rustovské části projektu by měl proběhnout bez větších problémů:
cargo build Compiling test1 v0.1.0 (file:///home/tester/libs/test1) Finished debug [unoptimized + debuginfo] target(s) in 0.40 secs
Dynamická knihovna by se měla objevit v podadresáři target/debug a měla by mít název libtest1.so (na Windows pravděpodobně test1.dll, ale nemám to kde odzkoušet :-)
Můžete si vytvořit i finální verzi knihovny:
$ cargo build --release Compiling test1 v0.1.0 (file:///home/tester/libs/test1) Finished release [optimized] target(s) in 0.44 secs
V tomto případě bude dynamická knihovna vytvořena v podadresáři target/release.
Poznámka: po otestování (viz navazující kapitoly) je možné adresář s projektem vyčistit příkazem:
cargo clean
Pokud si ale zkusíte spustit skript test.py, dočkáte se nemilého překvapení v podobě pádu skriptu (a výpisu stacktrace):
Traceback (most recent call last): File "./test.py", line 6, in <module> result = testlib1.add_integers(1, 2) File "/usr/lib/python3.4/ctypes/__init__.py", line 364, in __getattr__ func = self.__getitem__(name) File "/usr/lib/python3.4/ctypes/__init__.py", line 369, in __getitem__ func = self._FuncPtr((name_or_ordinal, self)) AttributeError: target/debug/libtest1.so: undefined symbol: add_integers
7. Name mangling a jeho důsledky
Proč vlastně došlo k této chybě? Už v úvodním textu jsme si řekli, že při překladu zdrojového kódu Rustu do nativního kódu je nutné zamezit takzvanému name manglingu. Pokud se totiž podíváme na obsah vytvořené dynamické knihovny, tak zjistíme, že naše funkce nazvaná původně add_integers byla během překladu přejmenována:
objdump -t target/debug/libtest1.so |grep add_integers 00000000000c0be0 g F .text 0000000000000040 _ZN5test112add_integers17hb1df977e169afd6aE
Skript psaný v Pythonu se snaží zavolat nativní funkci pojmenovanou _add_integers, ovšem tu nenajde, našel by teoreticky jen _ZN5test112add_integers17hb1df977e169afd6aE.
8. Zákaz name manglingu při překladu
Ve skutečnosti je možné name mangling velmi jednoduše zakázat, minimálně pro naši funkci, která akceptuje dva celočíselné (tedy primitivní) parametry a vrací taktéž celočíselný parametr. Postačuje nepatrná úprava zdrojového kódu:
#[no_mangle] pub extern fn add_integers(x: i32, y: i32) -> i32 { x + y }
Po překladu si zkontrolujeme, zda byla funkce zařazena do dynamické knihovny a jaké je její skutečné jméno:
$ objdump -t target/debug/libtest2.so |grep add_integers 00000000000c0bc0 g F .text 0000000000000040 add_integers
Vidíme, že nyní již jméno neobsahuje žádné „magické“ znaky, takže tato část aplikace je v pořádku.
Po spuštění Pythonovské části:
python test.py
Dostaneme následující výstup, který je zcela v pořádku (resp. je očekávaný):
1 + 2 = 3 Traceback (most recent call last): File "./test.py", line 9, in <module> result = testlib2.add_integers(1.5, 2) ctypes.ArgumentError: argument 1: <class 'TypeError'>: Don't know how to convert parameter 1
První volání funkce add_integers() proběhlo v pořádku, avšak volání druhé skončilo s chybou, protože se nativní funkci snažíme předat neceločíselný parametr (což je samozřejmě nekorektní, takže pád skriptu je očekávaný a jedná se o mnohem lepší chování, než kdyby se Python snažil parametry nějakým způsobem implicitně převádět).
9. Načtení dynamické knihovny bez uvedení cesty ve zdrojovém kódu
Většinou se budeme v praxi snažit, aby se cesta k dynamické knihovně nemusela do zdrojového kódu nikam zadávat, takže se namísto skriptu:
#!/usr/bin/env python3 import ctypes testlib1 = ctypes.CDLL("target/debug/libtest1.so") ... ... ...
Použije spíše skript:
#!/usr/bin/env python3 import ctypes testlib2 = ctypes.CDLL("libtest2.so") result = testlib2.add_integers(1, 2) print("1 + 2 = {}".format(result)) result = testlib2.add_integers(1.5, 2) print("1.5 + 2 = {}".format(result))
V případě, že se pokusíme tento skript spustit, dojde k chybě, protože knihovna, kterou jsme přeložili, byla uložena do podadresáře, o němž skript nic neví:
Traceback (most recent call last): File "./test2.py", line 4, in <module> testlib2 = ctypes.CDLL("libtest2.so") File "/usr/lib/python3.4/ctypes/__init__.py", line 351, in __init__ self._handle = _dlopen(self._name, mode) OSError: libtest2.so: cannot open shared object file: No such file or directory
Pokud však před spuštěním skriptu nastavíme proměnnou prostředí LD_LIBRARY_PATH, knihovna se bez problému nalezne a skript se spustí bez chyby:
LD_LIBRARY_PATH=target/debug ./test2.py
Poznámka: mnohem korektnější by samozřejmě bylo pouze přidat nový adresář do existujícího obsahu proměnné LD_LIBRARY_PATH, tj. provést tento příkaz:
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:target/debug ./test2.py
10. Předávání struktur mezi Rustem a Pythonem
Ve druhé části dnešního článku si ukážeme způsob předávání struktur mezi Rustem a Pythonem. Připomeňme si, že v Rustu představují struktury základní (a do značné míry jedinou) technologii pro tvorbu uživatelsky definovaných heterogenních datových struktur (naproti tomu pole a vektory jsou struktury homogenní). Oproti primitivním datovým typům je předávání struktur poněkud složitější, a to zejména na straně Pythonu, protože je nutné explicitně specifikovat typy prvků a samozřejmě i jejich pořadí. Další problém, který je někdy nutné řešit, představuje předávání struktur odkazem, tj. s využitím ukazatelů. I s touto problematikou se postupně seznámíme.
11. Rustovská část aplikace
Ukažme si nyní velmi jednoduchou aplikaci, v níž bude používána datová struktura reprezentující komplexní číslo a v Rustu bude navíc implementována funkce pro součet dvou komplexních čísel.
Část aplikace psaná v programovacím jazyku Rust bude nejprve obsahovat deklaraci struktury nazvané Complex. Tuto strukturu již známe a jedinou změnou je přidání anotace zajišťující překlad podle zvyků programovacího jazyka C (protože knihovna ctypes počítá s céčkovými konvencemi):
#[repr(C)] pub struct Complex { real: f32, imag: f32, }
Ve stejném zdrojovém kódu je taktéž uvedena funkce určená pro součet dvou komplexních čísel. Jedná se o skutečnou funkci, která nijak nemění své parametry, ale vytváří nové komplexní číslo (to ovšem nemusí být příliš efektivní, například při práci s vektory či maticemi komplexních čísel):
#[no_mangle] pub extern fn add_complex(c1: Complex, c2: Complex) -> Complex { Complex {real: c1.real + c2.real, imag: c1.imag + c2.imag} }
12. Skript psaný v Pythonu, který volá funkce z dynamické knihovny
Skript naprogramovaný v Pythonu, který bude volat rustovskou funkci pro součet komplexních čísel, je již poměrně složitý. Nejdříve si uveďme je úplnou podobu a potom se zaměříme na popis jednotlivých částí:
#!/usr/bin/env python3 import ctypes libtest3 = ctypes.CDLL("target/debug/libtest3.so") class Complex(ctypes.Structure): _fields_ = [("real", ctypes.c_float), ("imag", ctypes.c_float)] def __str__(self): return "Complex: %f + i%f" % (self.real, self.imag) libtest3.add_complex.argtypes = (Complex, Complex) libtest3.add_complex.restype = Complex c1 = Complex(1.0, 2.0) c2 = Complex(3.0, 4.0) c3 = libtest3.add_complex(c1, c2) print(c1) print(c2) print(c3)
Na začátku pouze naimportujeme funkce a typy z modulu ctypes:
#!/usr/bin/env python3 import ctypes
Následně se pokusíme načíst novou dynamickou knihovnu s deklarací struktury Complex i s funkcí add_complex(). Samozřejmě zde můžete odstranit cestu ke knihovně a použít přístup s proměnnou prostředí LD_LIBRARY_PATH:
libtest3 = ctypes.CDLL("target/debug/libtest3.so")
Následuje poměrně složitá část skriptu, v níž (znovu) deklarujeme datovou strukturu Complex, tentokrát ovšem takovým způsobem, aby pořadí a typy atributů (fields) přesně odpovídaly rustovské deklaraci. Všimněte si, jak se atributy popisují – uvádí se jejich jméno a datový typ (což je důležité, aby interpret Pythonu mohl strukturu vytvořit tak, aby byla binárním obrazem céčkovské či rustovské struktury). Navíc si – zcela nezávisle na původní struktuře – můžeme přidat metody, například metodu __str__, která nám umožní nechat si vypsat obsah komplexního číslo (tedy reálné a imaginární složky).
class Complex(ctypes.Structure): _fields_ = [("real", ctypes.c_float), ("imag", ctypes.c_float)] def __str__(self): return "Complex: %f + i%f" % (self.real, self.imag)
Od této chvíle je možné se k třídě Complex chovat prakticky stejně jako k jakékoli jiné třídě.
Následuje ještě jedná poměrně záludná, ale důležitá část, a to konkrétní určení typů parametrů funkce add_complex() a taktéž přesného návratového typu této funkce. Připomeňme si, že libtest3 je objekt vrácený voláním ctypes.CDLL(„target/debug/libtest3.so“):
libtest3.add_complex.argtypes = (Complex, Complex) libtest3.add_complex.restype = Complex
Nyní se již můžeme začít chovat ke třídě Complex i k funkci add_complex() běžným způsobem – následující kód již neobsahuje žádné speciality a při pohledu na něj ani nelze říct, že by třída Complex či funkce add_complex() byla něčím výjimečná:
c1 = Complex(1.0, 2.0) c2 = Complex(3.0, 4.0) c3 = libtest3.add_complex(c1, c2) print(c1) print(c2) print(c3)
13. Spuštění skriptu, který zavolá funkci pro součet komplexních čísel
Po spuštění skriptu by se měly na standardním výstupu objevit hodnoty uložené do datových struktur c1 a c2 i hodnoty ve vypočtené struktuře c3:
Complex: 1.000000 + i2.000000 Complex: 3.000000 + i4.000000 Complex: 4.000000 + i6.000000
14. Předávání struktur odkazem
Velmi často se setkáme s nutností předat strukturu do volané funkce odkazem. Opět se nejdříve podívejme na rustovskou část aplikace, v níž je deklarována nová funkce, které se předává první komplexní číslo odkazem, což nám umožňuje měnit jeho atributy (navíc se při volání nemusí struktura kopírovat):
#[repr(C)] pub struct Complex { real: f32, imag: f32, } #[no_mangle] pub extern fn add_complex(c1: Complex, c2: Complex) -> Complex { Complex {real: c1.real + c2.real, imag: c1.imag + c2.imag} } #[no_mangle] pub extern fn add_complex_mut(c1: &mut Complex, c2: Complex) -> () { c1.real += c2.real; c1.imag += c2.imag; }
Skript napsaný v Pythonu je nepatrně složitější, minimálně v té části, kde se specifikují typy parametrů funkce add_complex_mut() (viz zvýrazněnou část):
#!/usr/bin/env python3 import ctypes libtest4 = ctypes.CDLL("target/debug/libtest4.so") class Complex(ctypes.Structure): _fields_ = [("real", ctypes.c_float), ("imag", ctypes.c_float)] def __str__(self): return "Complex: %f + i%f" % (self.real, self.imag) libtest4.add_complex.argtypes = (Complex, Complex) libtest4.add_complex.restype = Complex libtest4.add_complex_mut.argtypes = (ctypes.POINTER(Complex), Complex) libtest4.add_complex_mut.restype = None c1 = Complex(1.0, 2.0) c2 = Complex(3.0, 4.0) c3 = libtest4.add_complex(c1, c2) print(c1) print(c2) print(c3) libtest4.add_complex_mut(ctypes.byref(c1), c2) print(c1) libtest4.add_complex_mut(ctypes.byref(c1), c2) print(c1)
V závěrečné části skriptu se musí objekt (instance třídy Complex) převést na referenci (resp. se musí předat reference).
15. Repositář s demonstračními příklady
Všechny čtyři dnes popisované demonstrační příklady (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. 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):
16. Odkazy na Internetu
- Calling Rust From Python
https://bheisler.github.io/post/calling-rust-in-python/ - Calling Rust in Python (komentáře k předchozímu článku)
https://www.reddit.com/r/rust/comments/63iy5a/calling_rust_in_python/ - CFFI Documentation
https://cffi.readthedocs.io/en/latest/ - 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 - ctypes — A foreign function library for Python
https://docs.python.org/2/library/ctypes.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