Hlavní navigace

Programovací jazyk Rust: rozhraní mezi Rustem a Pythonem

Pavel Tišnovský

V dnešním článku o jazyku Rust si ukážeme překlad zdrojového kódu Rustu do dynamické knihovny a taktéž to, jak se budou funkce Rustu volat z Pythonu. Při komunikaci mezi jazyky je samozřejmě nutné vyřešit několik problémů.

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

11. Rustovská část aplikace

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

16. Odkazy na Internetu

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_integers17hb1df977e169af­d6aE.

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

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