Hlavní navigace

Programovací jazyk Rust: rozhraní mezi Rustem a Pythonem (pokračování)

Pavel Tišnovský

Navážeme na předchozí článek, v němž jsme si popsali základní způsob použití rozhraní mezi Pythonem a Rustem. Nejdříve si ukážeme předávání řetězců z Pythonu do Rustu i naopak a pak se zaměříme na předávání řezů (slices).

Obsah

1. Programovací jazyk Rust: rozhraní mezi Rustem a Pythonem (pokračování)

2. Rozhraní mezi Pythonem a Rustem při předávání řetězců

3. Kdo se postará o uvolnění řetězce z operační paměti?

4. Předání řetězce z Pythonu do Rustu

5. Část aplikace naprogramovaná v Rustu

6. Testovací skript určený pro Python 2.x

7. Testovací skript určený pro Python 3.x

8. Vytvoření řetězce v Rustu s jeho použitím v Pythonu

9. Část aplikace naprogramovaná v Rustu

10. Testovací skript určený pro Python

11. Předání „slice“ z Pythonu do Rustu

12. Část aplikace naprogramovaná v Rustu

13. Testovací skript určený pro Python

14. Vylepšení aplikace pro předání „slice“ z Pythonu do Rustu

15. Repositář s demonstračními příklady

16. Odkazy na Internetu

1. Programovací jazyk Rust: rozhraní mezi Rustem a Pythonem (pokračování)

V minulé části seriálu o programovacím jazyku Rust jsme si vysvětlili, jakým způsobem je možné zajistit komunikaci mezi částí aplikace naprogramovanou v Rustu a částí naprogramovanou v Pythonu. Popsaný princip byl poměrně jednoduchý – část psaná v Rustu se musela přeložit do nativního (strojového) kódu takovým způsobem, aby nedocházelo k name manglingu jmen funkcí; překlad navíc musel být proveden do statické či ještě lépe dynamické knihovny. Na straně programovacího jazyka Python se použil modul ctypes popř. alternativně CFFI, který dynamickou knihovnu načetl, umožnil přesně specifikovat typy argumentů nativních funkcí i jejich návratové typy a nakonec umožnil zavolání nativních funkcí.

Připomeňme si, že u funkcí akceptujících argumenty primitivních datových typů bylo použití ctypes velmi jednoduché:

pub extern fn add_integers(x: i32, y: i32) -> i32 {
    x + y
}

Skript napsaný v Pythonu mohl tuto nativní funkci volat následovně:

#!/usr/bin/env python3
import ctypes
 
testlib1 = ctypes.CDLL("cesta/ke/knihovně/libtest1.so")
 
result = testlib1.add_integers(1, 2)
print("1 + 2 = {}".format(result))

Poněkud složitější je předávání struktur odkazem:

#[repr(C)]
pub struct Complex {
    real: f32,
    imag: f32,
}
 
#[no_mangle]
pub extern fn add_complex_mut(c1: &mut Complex, c2: Complex) -> () {
    c1.real += c2.real;
    c1.imag += c2.imag;
}

Zde již skript psaný v Pythonu potřebuje explicitní informace o typech parametrů:

#!/usr/bin/env python3
import ctypes
 
testlib4 = ctypes.CDLL("cesta/ke/knihovně/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_mut.argtypes = (ctypes.POINTER(Complex), Complex)
libtest4.add_complex_mut.restype = None
 
c1 = Complex(1.0, 2.0)
c2 = Complex(3.0, 4.0)
 
libtest4.add_complex_mut(ctypes.byref(c1), c2)
print(c1)

2. Rozhraní mezi Pythonem a Rustem při předávání řetězců

Velmi často se setkáme s nutností předávání řetězců mezi Pythonem a Rustem. Může se jednat o předání řetězců z Pythonu do Rustu či naopak o získání řetězce z Rustu skriptem psaným v Pythonu. To, zda je řetězec vytvořen v Pythonu či v Rustu, má velký vliv na to, jak se k řetězci budeme chovat, protože oba programovací jazyky používají odlišný způsob alokace a dealokace objektů, takže může docházet k různým typům problémů, od memory leaků až po pády aplikace. Další potenciální problémy mohou být způsobeny tím, že rozhraní mezi Pythonem a Rustem je (nejenom) z historických důvodů založeno na typovém systému céčka, takže vlastně budeme pracovat s ukazateli na pole znaků. Naopak prakticky bezproblémové je řešení problematiky kódování znaků v řetězcích, protože jak programovací jazyk Rust, tak i Python (Python 3 navíc i implicitně) používají Unicode a kódování UTF-8.

3. Kdo se postará o uvolnění řetězce z operační paměti?

Programovací jazyk Rust používá pro určení, zda je nějaký objekt možné uvolnit z paměti, viditelnost objektu určovanou v době překladu. Python (přesněji řečeno v tomto kontextu CPython) naproti tomu používá klasický garbage collector. Pokud je řetězec vytvořen v Pythonu a předán do kódu psaného v Rustu, je předání typicky provedeno přes ukazatel a tudíž překladač Rustu ví, že řetězec (resp. přesněji řečeno libovolný objekt předaný přes ukazatel) nevlastní a tudíž nijak nehlídá jeho viditelnost/životnost. To je ve skutečnosti velmi dobré chování, neboť o odstranění řetězce se postará runtime Pythonu a tudíž nedojde k pokusu o dvojí dealokaci. Musíme ovšem sami zajistit, aby část programu naprogramovaná v Rustu k originálnímu řetězci již nepřistupovala, takže se typicky musí provést explicitní kopie (ve skutečnosti se ovšem většinou s řetězcem pracuje v jediné funkci, takže ani to není nutné). Pokud je naopak řetězec vytvořen v Rustu, musíme ho předat (vrátit) do Pythonu přes ukazatel a explicitně se postarat o jeho uvolnění!

4. Předání řetězce z Pythonu do Rustu

V dnešním prvním demonstračním příkladu se pokusíme vytvořit řetězec v Pythonu (což samozřejmě není nic složitého) a následně ho předat do funkce naprogramované v Rustu. Pro jednoduchost rustovská funkce řetězec pouze vytiskne a neprovede s ním žádné další operace. Předání bude vypadat zhruba následovně:

+-------------------------+
|    řetězec v Pythonu    |
+-------------------------+
            ⇓
+-------------------------+
|     sekvence bajtů      |
+-------------------------+
            ⇓
+-------------------------+
|          char *         |
+-------------------------+
            ⇓
+-------------------------+
|          CStr           |
+-------------------------+
            ⇓
+-------------------------+
| Result<&str, Utf8Error> |
+-------------------------+
            ⇓
+-------------------------+
|          &str           |
+-------------------------+

To sice nevypadá příliš jednoduše, na druhou stranu jsou však jednotlivé konverze otázkou jediného řádku kódu.

5. Část aplikace naprogramovaná v Rustu

Podívejme se, jak vypadá rustovská část aplikace. V ní vytvoříme funkci nazvanou print_string(), která akceptuje jediný parametr typu *const c_char, což je nejbližší obdoba const char * v programovacím jazyku C. Nyní z tohoto ukazatele vytvoříme hodnotu typu CStr, která je chápána jako borrowed, tj. funkce ji nebude vlastnit a nedojde k pokusu o automatickou dealokaci. Následně tento typ převedeme na běžný slice &str, s nímž je již možné běžně pracovat. Povšimněte si volání unwrap(), které je zde nutné, protože ve skutečnosti metoda to_str() vrací typ Result<&str, Utf8Error>, jelikož při pokusu o konverzi libovolné sekvence bajtů do UTF-8 může dojít k chybám:

use std::ffi::CStr;
use std::os::raw::c_char;
 
#[no_mangle]
pub extern fn print_string(what: *const c_char) -> () {
    unsafe {
        let c_string = CStr::from_ptr(what).to_str().unwrap();
        println!("{:?}", c_string);
    }
}

6. Testovací skript určený pro Python 2.x

Testovací skript nyní bude existovat ve dvou verzích, z nichž první je určena pro Python 2.x a druhá pro Python 3.x. V Pythonu 2.x je nejprve nutné specifikovat typ parametru funkce print_string, zde konkrétně použijeme typ c_char_p. Následně je již možné funkci zavolat a předat jí řetězec přetypovaný na c_char_p:

#!/usr/bin/env python2
# vim: set fileencoding=utf-8
 
import ctypes
 
libtest5 = ctypes.CDLL("target/debug/libtest5.so")
 
libtest5.print_string.argtypes = (ctypes.c_char_p,)
 
libtest5.print_string(ctypes.c_char_p("Hello world!"))
libtest5.print_string(ctypes.c_char_p("Příliš žluťoučký kůň"))
libtest5.print_string(ctypes.c_char_p("Ну, погоди!"))

Ve skutečnosti můžeme v Pythonu 2 celý zápis ještě více zkrátit a volat nativní funkci print_string() bez explicitního přetypování:

#!/usr/bin/env python2
# vim: set fileencoding=utf-8
 
import ctypes
 
libtest5 = ctypes.CDLL("target/debug/libtest5.so")
 
libtest5.print_string.argtypes = (ctypes.c_char_p,)
 
libtest5.print_string("Hello world!")
libtest5.print_string("Příliš žluťoučký kůň")
libtest5.print_string("Ну, погоди!")

Povšimněte si, že bez problémů používáme Unicode.

7. Testovací skript určený pro Python 3.x

V Pythonu 3 je nutné provést nepatrné úpravy, protože převod řetězce (zde Unicode řetězce) na sekvenci bajtů vyžaduje explicitní volání metody str.encode() s určením konkrétního kódování:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import ctypes
 
libtest5 = ctypes.CDLL("target/debug/libtest5.so")
 
libtest5.print_string.argtypes = (ctypes.c_char_p,)
 
libtest5.print_string("Hello world!".encode('utf-8'))
libtest5.print_string("Příliš žluťoučký kůň".encode('utf-8'))
libtest5.print_string("Ну, погоди!".encode('utf-8'))

Vzhledem k tomu, že pro encode() je kódování UTF-8 implicitní, lze skript ještě zjednodušit:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import ctypes
 
libtest5 = ctypes.CDLL("target/debug/libtest5.so")
 
libtest5.print_string.argtypes = (ctypes.c_char_p,)
 
libtest5.print_string("Hello world!".encode())
libtest5.print_string("Příliš žluťoučký kůň".encode())
libtest5.print_string("Ну, погоди!".encode())

8. Vytvoření řetězce v Rustu s jeho použitím v Pythonu

Předchozí příklad byl poměrně jednoduchý, protože řetězec byl vytvořen v Pythonu, jehož běhové prostředí se posléze automaticky postaralo o jeho uvolnění z operační paměti. Co se však stane v opačném případě, tj. ve chvíli, kdy řetězec vytvoříme v Rustu a předáme ho (jako návratovou hodnotu) do Pythonu? V takovém případě musíme sami (!) zajistit následující chování:

  1. Rust nesmí řetězec automaticky dealokovat při opuštění funkce či bloku.
  2. Python taktéž nesmí řetězec automaticky dealokovat (nepatří mu, navíc provádí alokace a dealokace jiným způsobem).
  3. Musíme vytvořit funkci pro dealokaci řetězce (v Rustu) a tu explicitně zavolat.

Řešením je použití takzvaných raw pointerů, kdy se při vytvoření raw pointeru z nějakého objektu explicitně vzdáváme vlastnictví (ownership).

9. Část aplikace naprogramovaná v Rustu

Část aplikace naprogramovaná v Rustu je nyní delší, protože obsahuje jak funkci pro vytvoření řetězce (zde pro ilustraci řetězce se sekvencí hvězdiček), tak i funkci, která řetězec bude dealokovat z operační paměti. Povšimněte si, že nyní z řetězce (typ String) vytváříme objekt CString a z něho získáme raw pointer. Raw pointer je současně návratovou hodnotou funkce a navíc se řetězec neodstraní z paměti před návratem z funkce (což je implicitní chování Rustu). Naopak funkce pro uvolnění řetězce dealokaci provádí, ale implicitně – z raw pointeru vytvoříme objekt CString (tím současně získáme i vlastnictví/ownership) a ten ihned zahodíme – a právě v této chvíli může Rust provést dealokaci:

use std::iter;
use std::ffi::CString;
use std::os::raw::c_char;
 
#[no_mangle]
pub extern fn generate_stars(count: u8) -> *mut c_char {
    let s: String = iter::repeat("*").take(count as usize).collect();
    let c_string = CString::new(s).unwrap();
    let raw = c_string.into_raw();
    println!("output raw pointer:  {:?}", raw);
    raw
}
 
#[no_mangle]
pub extern fn free_string(raw: *mut c_char) {
    unsafe {
        if raw.is_null() { return }
        println!("raw pointer to free: {:?}", raw);
        CString::from_raw(raw)
    };
}

Poznámka: obecně platí, že funkce Objekt.into_raw() a from_raw() se musí vyskytovat ve dvojici. Pokud tomu tak není (volá se jen into_raw()), bude v aplikaci pravděpodobně docházet k memory leakům.

10. Testovací skript určený pro Python

I skript naprogramovaný v Pythonu je nepatrně delší, protože musíme zajistit volání funkce free_string(), jelikož jinak by řetězec nikdy nebyl uvolněn z paměti (což samozřejmě bude vadit ve chvíli, kdy funkci generate_stars() voláme častěji popř. se jedná o aplikaci běžící po dlouhou dobu). Uvolnění řetězce zajistíme jednoduše konstrukcí try-(return)-finally:

#!/usr/bin/env python2
# vim: set fileencoding=utf-8
 
import ctypes
 
libtest6 = ctypes.CDLL("target/debug/libtest6.so")
 
libtest6.generate_stars.argtypes = (ctypes.c_uint8, )
libtest6.generate_stars.restype = ctypes.c_void_p
 
# pozor na nutnost uvedení čárky - máme n-tici s jediným prvkem
libtest6.free_string.argtypes = (ctypes.c_void_p, )
 
def generate_stars(count):
    pointer = libtest6.generate_stars(count)
    try:
        return ctypes.cast(pointer, ctypes.c_char_p).value.decode('utf-8')
    finally:
        libtest6.free_string(pointer)
 
print(generate_stars(42))

11. Předání „slice“ z Pythonu do Rustu

V závěrečné třetině článku si ukážeme, jakým způsobem je možné předat řez (slice) z Pythonu do Rustu. Řezy jsou velmi užitečnou datovou strukturou, protože nabízí stejně efektivní způsob uložení prvků i stejně efektivní přístup k prvkům jako pole a současně obsahují i počet prvků (resp. délku řezu, protože délka závisí jak na počtu prvků, tak i na jejich velikosti). To mj. znamená, že je možné řezy použít pro předávání n-tic nebo seznamů z Pythonu do Rustu. Ve skutečnosti však nebude řez předán jediným parametrem, ale parametry dvěma – ukazatelem na data (pole) a velikostí řezu. Z těchto dvou parametrů pak funkcí from_raw_parts(p: *const T, len: usize) → &'a [T] vytvoříme řez, s nímž je možné dále pracovat. O dealokaci se nemusíme starat, neboť tu zajistí běhové prostředí Pythonu; to však samozřejmě znamená, že rustovská část aplikace musí počítat s tím, že po opuštění volané funkce může řez přestat existovat (tudíž si na něj nesmí vytvářet ukazatele, předávat ownership atd.)

12. Část aplikace naprogramovaná v Rustu

V části aplikace naprogramované v Rustu je deklarována funkce, která očekává ukazatel na prvky typu i32 a taktéž počet prvků řezu. S těmito údaji je zavolána již výše zmíněná funkce from_raw_parts() (navíc se délka musí přetypovat). Získaný řez použijeme při výpočtu součtu všech prvků, které se v řezu nachází. Výsledný součet je současně i návratovou hodnotou funkce:

use std::slice;
 
#[no_mangle]
pub extern fn sum(items: *const i32, length: usize) -> i32 {
    let numbers = unsafe {
        slice::from_raw_parts(items, length as usize)
    };
 
    let mut sum:i32 = 0;
    for number in numbers {
        sum += *number
    }
    sum
}

Poznámka: length musí obsahovat počet prvků, nikoli délku řezu v bajtech. Převod na bajty si provede funkce from_raw_parts() sama.

13. Testovací skript určený pro Python

Testovací skript naprogramovaný v Pythonu obsahuje deklaraci pomocné funkce sum(), jejímž účelem je získat seznam či sekvenci a z ní vytvořit dvojici ukazatel_na_první_prvek+délka (počet prvků, ne počet bajtů). Tyto dva údaje jsou předány rustovské funkci:

#!/usr/bin/env python2
# vim: set fileencoding=utf-8
 
import ctypes
 
libtest7 = ctypes.CDLL("target/debug/libtest7.so")
 
libtest7.sum.argtypes = (ctypes.POINTER(ctypes.c_uint32), ctypes.c_size_t)
libtest7.sum.restype = ctypes.c_int32
 
def sum(numbers):
    buf_type = ctypes.c_uint32 * len(numbers)
    buf = buf_type(*numbers)
    return libtest7.sum(buf, len(numbers))
 
print(sum([1,2,3,4]))
 
print(sum(range(11)))

Všimněte si, že do funkce sum() můžeme předat jak seznam, tak i sekvenci (v Pythonu 3).

14. Vylepšení aplikace pro předání „slice“ z Pythonu do Rustu

Nativní část aplikace psanou v Rustu je možné vylepšit. Nejprve do bloku usafe přidáme aserci testující, zda se náhodou funkce nevolá s prvním parametrem nastaveným na NULL. To obecně v Rustu není možné, ovšem ukazatele získané z jiných jazyků jsou v tomto ohledu výjimečné. Mimochodem si povšimněte, že celý blok unsafe je ukončen výrazem (na jeho konci není středník), jehož výsledek se uloží do proměnné numbers. Druhá úprava spočívá ve výpočtu sumy napsaném na jediném řádku funkcionálním stylem, s nímž jsme se již v Rustu několikrát setkali. Opět si povšimněte, že celý řádek je výrazem, jehož výsledná hodnota je současně výsledkem celé funkce:

use std::slice;
 
#[no_mangle]
pub extern fn sum(items: *const i32, length: usize) -> i32 {
    let numbers = unsafe {
        assert!(!items.is_null());
 
        slice::from_raw_parts(items, length as usize)
    };
 
    numbers.iter().fold(0, |acc, v| acc + v)
}

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):

Příklad Adresa
Knihovna číslo 5  
Cargo.toml https://github.com/tisnik/pre­sentations/blob/master/rus­t/libs/test5/Cargo.toml
lib.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/libs/test4/src/lib.rs
test_python2.py https://github.com/tisnik/pre­sentations/blob/master/rus­t/libs/test5/test_python2­.py
test_python2_2.py https://github.com/tisnik/pre­sentations/blob/master/rus­t/libs/test5/test_python2_2­.py
test_python3.py https://github.com/tisnik/pre­sentations/blob/master/rus­t/libs/test5/test_python3­.py
test_python3_2.py https://github.com/tisnik/pre­sentations/blob/master/rus­t/libs/test5/test_python3_2­.py
   
Knihovna číslo 6  
Cargo.toml https://github.com/tisnik/pre­sentations/blob/master/rus­t/libs/test6/Cargo.toml
lib.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/libs/test6/src/lib.rs
test.py https://github.com/tisnik/pre­sentations/blob/master/rus­t/libs/test6/test.py
   
Knihovna číslo 7  
Cargo.toml https://github.com/tisnik/pre­sentations/blob/master/rus­t/libs/test7/Cargo.toml
lib.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/libs/test7/src/lib.rs
test.py https://github.com/tisnik/pre­sentations/blob/master/rus­t/libs/test7/test.py
   
Knihovna číslo 8  
Cargo.toml https://github.com/tisnik/pre­sentations/blob/master/rus­t/libs/test8/Cargo.toml
lib.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/libs/test8/src/lib.rs
test.py https://github.com/tisnik/pre­sentations/blob/master/rus­t/libs/test8/test.py

16. Odkazy na Internetu

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