Hlavní navigace

Programovací jazyk Rust: použití FFI pro volání funkcí z nativních knihoven

Pavel Tišnovský

V programovacím jazyku Rust se poměrně často setkáme se situacemi, v nichž je nutné použít nativní knihovny. V takovém případě se využívá FFI. Dnes si ukážeme, jak se funkce z nativních knihoven volají.

Obsah

1. Programovací jazyk Rust: použití FFI pro volání funkcí z nativních knihoven

2. Céčkový zdrojový kód s nativní funkcí

3. Překlad funkce do objektového souboru a vytvoření statické knihovny

4. Použití statické knihovny v Rustu

5. Překlad a spuštění aplikace psané v Rustu, která volá funkci ze statické knihovny

6. Zjednodušení procesu překladu a spouštění: jednoduchý Makefile

7. Překlad funkce do objektového souboru a vytvoření dynamické knihovny

8. Použití dynamické knihovny v Rustu

9. Překlad a spuštění aplikace psané v Rustu, která volá funkci z dynamické knihovny

10. Pomocný soubor Makefile pro druhý příklad

11. Problematika řetězců – C versus Rust

12. Předání řetězce do volané nativní funkce

13. Dangling pointers a jak je nevytvářet

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

15. Odkazy na Internetu

1. Programovací jazyk Rust: použití FFI pro volání funkcí z nativních knihoven

V Rustu, podobně jako v mnoha dalších programovacích jazycích (Python, Lua, Common Lisp, Pixie…), je možné volat funkce z nativních knihoven (slovem „nativní“ je zde myšlena knihovna vytvořená pro danou architekturu mikroprocesoru). Tyto funkce mohou být naprogramovány například v jazyku C či C++, přeloženy a jejich objektový kód naimportován do statické či dynamické knihovny. Dnes si ukážeme, jak se takové knihovny (statickédynamické) vytváří a jakým způsobem se nativním funkcím předávají parametry. Zmíníme se i o velmi často řešené problematice – předávání řetězců do nativních funkcí.

2. Céčkový zdrojový kód s nativní funkcí

Pro jednoduchost předpokládejme, že budeme potřebovat zavolat nativní funkci, která sečte dvě celá čísla (se znaménkem) a vrátí vypočtený výsledek. První verze této funkce (naprogramované v céčku) může vypadat následovně:

int add(int x, int y)
{
    return x+y;
}

Takovou funkci je samozřejmě možné přeložit a použít, ovšem při volání funkce add z Rustu se můžeme poměrně rychle dostat do problémů ve chvíli, kdy šířka datového typu int nebude přesně odpovídat datovému typu použitému na straně Rustu (připomeňme si, že Rust je portován na 32bitové i 64bitové CPU a navíc jeho variantu nalezneme i na šestnáctibitových mikrořadičích MSP430). Navíc ani nedochází ke kontrole, zda jsou šířky datových typů použity korektně. Proto je bezpečnější uvádět datové typy parametrů funkce i její návratové hodnoty zcela explicitně, například takto:

#include <stdint.h>
 
int32_t add(int32_t x, int32_t y)
{
    return x+y;
}

Převodní tabulka mezi celočíselnými typy v C a typy Rustu:

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ů

3. Překlad funkce do objektového souboru a vytvoření statické knihovny

V prvním kroku céčkovský zdrojový kód přeložíme překladačem gcc (lze však použít i clang), a to následujícím způsobem:

gcc -Wall -ansi -c ffi1.c -o ffi1.o

Poznámka: na rozdíl od druhého příkladu nemusíme při kompilaci uvádět přepínač -fPIC zajištující vygenerování pozičně nezávislého strojového kódu (Position-Independent Code).

V kroku druhém dojde k vytvoření statické knihovny. Pro tento účel použijeme nástroj ar s přepínači „r“ (přidání souboru do archivu představujícího statickou knihovnu), „c“ (vytvoření archivu/knihovny) a „s“ (vytvoření indexu). Přepínače se z historických důvodů mohou uvádět bez znaku pomlčky:

ar rcs libffi1.a ffi1.o

Pokud chcete zjistit, jaké symboly (popř. i kód) se v knihovně nachází, použijte nástroj nm:

nm -s libffi1.a 
 
Archive index:
add in ffi1.o
 
ffi1.o:
0000000000000000 T add

4. Použití statické knihovny v Rustu

Pokud budeme chtít nativní funkci add zavolat z Rustu, je nutné specifikovat, ve které knihovně je uložen strojový kód funkce a zda se jedná o knihovnu statickou či dynamickou. Dále je nutné v bloku extern {} uvést hlavičku funkce s použitím typů Rustu. Pro funkci se dvěma celočíselnými 32bitovými parametry se znaménkem, která vrací stejný typ (32bitové celé číslo se znaménkem) a která je uložena ve statické knihovně libffi1.a je deklarace následující:

#[link(name = "ffi1", kind="static")]
extern {
    fn add(x:i32, y:i32) -> i32;
}

Ze jména „ffi1“ je odvozen název knihovny, parametr „kind“ specifikuje knihovnu statickou (u dynamické knihovny se neuvádí).

Zavolání nativní funkce je snadné:

let x:i32 = 1;
let y:i32 = 2;
let z = add(x, y);

Ve skutečnosti však není volání nativní funkce bezpečné, což nám řekne překladač:

 --> ffi1.rs:9:13
  |
9 |     let z = add(x, y);
  |             ^^^^^^^^^ unsafe call requires unsafe function or block
 
error: aborting due to previous error

Z tohoto důvodu je nutné volání funkce umístit do bloku unsafe {}:

let x:i32 = 1;
let y:i32 = 2;
let z = unsafe { add(x, y) };

Celý příklad (resp. jeho rustovská část) bude vypadat následovně:

#[link(name = "ffi1", kind="static")]
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);
}

5. Překlad a spuštění aplikace psané v Rustu, která volá funkci ze statické knihovny

Při překladu zdrojového kódu psaného v Rustu se volá i linker, který musí vědět, kde je umístěna statická knihovna libffi1.a. Cestu ke knihovně/knihovnám můžeme předat přes parametr L, který bude obsahovat aktuální adresář (tedy tečku):

rustc -L . ffi1.rs

Díky tomu, že se program linkuje se statickou knihovnou (resp. jejím obsahem), není nutné soubor libffi1.a distribuovat současně s aplikací (používá se jen při vývoji).

Spuštění příkladu je již jednoduché:

$ ./ffi1
1 + 2 = 3

6. Zjednodušení procesu překladu a spouštění: jednoduchý Makefile

Aby nebylo nutné výše uvedené kroky provádět neustále dokola po každé úpravě části psané v jazyku C či naopak v Rusu, můžeme si vytvořit jednoduchý soubor Makefile, který překlad a slinkování automatizuje:

CC=gcc
CFLAGS=-Wall -ansi
RUSTC=rustc
 
APP=ffi1
PROGNAME=$(APP)
LIBNAME=lib$(APP).a
 
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)
        ar rcs $(LIBNAME) $<
 
%.o:    %.c
        $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
 
clean:
        rm -f *.o
        rm -f *.a
        rm -f $(PROGNAME)
 
run:
        ./$(APP)

7. Překlad funkce do objektového souboru a vytvoření dynamické knihovny

Mnohem častěji se setkáme s nutností volat nativní funkce uložené v dynamických knihovnách. Můžeme se tedy pokusit si vytvořit dynamickou knihovnu s naší funkcí add. Není to nic těžkého, ovšem je nutné dodržet několik pravidel, například překládat do pozičně nezávislého strojového kódu atd.

Postupovat je možné následujícím způsobem. Nejdříve se musí provést překlad céčkového zdrojového kódu do nativního objektového kódu, což se zdánlivě nijak neliší od předchozího příkladu, ovšem musíme zde nově použít přepínač -fPIC:

gcc -Wall -ansi -c -fPIC ffi2.c -o ffi2.o

Výsledkem je objektový soubor nazvaný ffi2.o. Následně se vytvoří dynamická knihovna příkazem:

gcc -shared -Wl,-soname,libffi2.so -o libffi2.so ffi2.o

Povšimněte si, že výsledná knihovna má prefix „lib“. To je důležité, neboť kdyby tento prefix nebyl použit a knihovna se jmenovala jen ffi2.so, nastaly by problémy s jejím načítáním (ty jsou samozřejmě řešitelné, ale proč si zbytečně přidělávat práci nestandardním pojmenováním?)

8. Použití dynamické knihovny v Rustu

Pokud se volá funkce z dynamické knihovny, je nutné program nepatrně upravit. Týká se to vlastně jen řádku:

#[link(name = "ffi1", kind="static")]

u nějž se vynechá specifikace kind a její hodnota. Je tomu tak z toho důvodu, že výchozím typem knihovny je knihovna dynamická:

#[link(name = "ffi2")]

Upravený program tedy bude vypadat následovně:

#[link(name = "ffi2")]
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);
}

I v tomto případě je nutné blok unsafe uvést, protože následující upravený příklad se nepřeloží:

#[link(name = "ffi2")]
extern {
    fn add(x:i32, y:i32) -> i32;
}
 
fn main() {
    let x:i32 = 1;
    let y:i32 = 2;
    let z = add(x, y);
    println!("{} + {} = {}", x, y, z);
}

Chybové hlášení překladače:

error[E0133]: call to unsafe function requires unsafe function or block
 --> ffi2.rs:9:14
  |
9 |     let z =  add(x, y);
  |              ^^^^^^^^^ unsafe call requires unsafe function or block
 
error: aborting due to previous error

9. Překlad a spuštění aplikace psané v Rustu, která volá funkci z dynamické knihovny

Překlad se provede nám již známým způsobem, tj. uvedením cesty k nativní linkované knihovně:

rustc -L . ffi2.rs

Spuštění ovšem bude komplikovanější, neboť se při snaze o prosté spuštění aplikace vypíše toto chybové hlášení oznamující, že v runtime nebylo možné nalézt požadovanou dynamickou knihovnu:

./ffi2: error while loading shared libraries: libffi2.so: cannot open shared object file: No such file or directory

Vzhledem k tomu, že námi vytvořená knihovna skutečně není umístěna do adresářů, v níž se sdílené knihovny hledají, musíme nastavit proměnnou prostředí LD_LIBRARY_PATH. Buď pro aktivní shell:

export LD_LIBRARY_PATH=.
./ffi2

nebo pouze pro jeden příkaz:

LD_LIBRARY_PATH=. ./ffi2

Pokud potřebujete zjistit, ve kterých adresářích se sdílené knihovny hledají, stačí použít příkaz (může si lišit podle typu distribuce):

cat /etc/ld.so.conf.d/*
 
/usr/lib/x86_64-linux-gnu/libfakeroot
/usr/lib/i386-linux-gnu/mesa
# Multiarch support
/lib/i386-linux-gnu
/usr/lib/i386-linux-gnu
/lib/i686-linux-gnu
/usr/lib/i686-linux-gnu
# libc default configuration
/usr/local/lib
/usr/local/lib
# Multiarch support
/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu/mesa-egl
/usr/lib/x86_64-linux-gnu/mesa

10. Pomocný soubor Makefile pro druhý příklad

CC=gcc
CFLAGS=-Wall -ansi -fPIC
RUSTC=rustc
 
APP=ffi2
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 $<
 
clean:
        rm -f *.o
        rm -f *.so
        rm -f $(PROGNAME)
 
run:
        export LD_LIBRARY_PATH=.;./$(APP)

11. Problematika řetězců – C versus Rust

V programovacím jazyku Rust existuje hned několik datových typů určených pro úschovu řetězců. Obecně se tyto typy rozdělují do dvou skupin podle toho, zda se jedná o takzvanou „slice“ variantu (sem spadají řetězce umístěné v kódovém segmentu) a o řetězce obalené vhodným kontejnerem a typicky umístěné na haldě. V každé skupině nalezneme čtyři typy: řetězce s kódováním UTF-8, řetězce kompatibilní s céčkem (nutno použít při volání céčkových knihoven), řetězce kompatibilní s operačním systémem (může se jednat o typ shodný s předchozím) a konečně řetězce, které mohou uchovávat cestu k souborům a adresářům (opět – nemusí se nutně jednat o zcela odlišný datový typ):

Vlastnost Varianta „slice“ Varianta „Owned“
UTF-8 string str String
Kompatibilní s C CStr CString
Kompatibilní s OS OsStr OsString
Cesta (v OS) Path PathBuf

Nás budou v tuto chvíli zajímat především řetězce kompatibilní s céčkem. Tyto řetězce interně vypadají odlišně od řetězců v Rustu, protože v céčku se namísto reference na pole znaků zkombinované s délkou pole používají řetězce ukončené nulou. Zkusme si vytvořit nativní funkci, která vrátí počet bajtů (ne nutně znaků!) v řetězci. Jedna z možných implementací může vypadat následovně:

#include <stdint.h>
 
int32_t string_length(const char *str)
{
    int32_t len = 0;
    for (; *str; str++, len++)
        ;
    return len;
}

Ve funkci postupně zvyšujeme ukazatel a testujeme, zda jsme nedosáhli konce řetězce, tedy bajtu s hodnotou nula. Současně zvyšujeme i počitadlo, jehož hodnota po ukončení smyčky obsahuje počet bajtů v řetězci.

Taková funkce se v Rustu popíše následovně (povšimněte si typu *const c_char):

#[link(name = "ffi3")]
extern {
    fn string_length(str: *const c_char) -> i32;
}

12. Předání řetězce do volané nativní funkce

Předání rustovského řetězce do céčka nelze provést přímo, ale je nejdříve nutné řetězec převést do kompatibilního objektu, tedy do struktury CString či CStr. K tomu slouží konstruktor CString:new() vracející typ Result<CString, NulError>:

CString::new("Hello world!").unwrap();

nebo lépe:

match CString::new("Hello world!") {
    Ok(string) => {
        ...
        ...
        ...
    }
    Err(error) => {
        println!("CString::new() error: {:?}", error);
    }
}

Následně se získá ukazatel na první znak v řetězci:

let pointer = string.as_ptr();

Teprve tento ukazatel se předá nativní funkci:

unsafe {
    println!("string length = {}", string_length(pointer));
}

Úplný zdrojový kód může vypadat následovně:

use std::ffi::CString;
use std::os::raw::c_char;
 
#[link(name = "ffi3")]
extern {
    fn string_length(str: *const c_char) -> i32;
}
 
fn main() {
    match CString::new("Hello world!") {
        Ok(string) => {
            let pointer = string.as_ptr();
            unsafe {
                println!("string length = {}", string_length(pointer));
            }
        }
        Err(error) => {
            println!("CString::new() error: {:?}", error);
        }
    }
}

Po překladu a spuštění se vypíše:

string length = 12

13. Dangling pointers a jak je nevytvářet

Dejte si pozor na to, že sice můžeme napsat:

let pointer = CString::new("Hello world!").unwrap().as_ptr();
unsafe {
    println!("string length = {}", string_length(pointer));
}

ale ve skutečnosti se nebude jednat o korektní program, protože platnost řetězce skončí ihned po provedení prvního výrazu a samotný ukazatel nenese – jak již víme z předchozích dvou dílů – žádnou informaci o životnosti hodnoty, na níž ukazuje! To znamená, že řetězec je po ukončení prvního výrazu odstraněn (resp. minimálně zneplatněn), ovšem o dva řádky dále používáme ukazatel na nyní již neplatná data!

Korektní způsob spočívá v rozdělení prvního řádku na dva:

let string = CString::new("Hello world!").unwrap();
let pointer = string.as_ptr();
unsafe {
    println!("string length = {}", string_length(pointer));
}

Nyní je již proměnná string platná v celém bloku.

14. 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/pre­sentations. Demonstrační příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý repositář:

Poznámka: poslední zmíněný příklad ffi4 si popíšeme příště.

15. Odkazy na Internetu

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