Hlavní navigace

Programovací jazyk Rust: použití FFI pro volání funkcí z nativních knihoven (2. část)

8. 6. 2017
Doba čtení: 24 minut

Sdílet

 Autor: Rust project
Na předchozí článek navážeme, protože se budeme zabývat dalšími detaily technologie FFI. Řekneme si, jak sdílet pole s nativními knihovnami, a taky si ukážeme nastavení projektu spravovaného systémem Cargo při použití FFI.

Obsah

1. Kódování znaků v řetězcích

2. Vypsání adresy ukazatele na straně Rustu i céčka

3. Předání adresy pole do funkce v nativní knihovně

4. Druhý demonstrační příklad – výpočet sumy prvků pole

5. Nedetekovaný problém při předání datové struktury přes ukazatel

6. Použití nástroje Cargo při práci s projekty používajícími nativní knihovny

7. Nastavení projektu spravovaného nástrojem Cargo

8. Soubor build.rs

9. Změny, které je nutné provést ve zdrojových kódech

10. Překlad a spuštění projektu

11. Zjednodušení skriptu pro překlad s použitím pluginu gcc

12. Upravený soubor build.rs

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

14. Odkazy na Internetu

1. Kódování znaků v řetězcích

Již v úvodních částech seriálu o programovacím jazyku Rust jsme si řekli, že textové řetězce jsou interně ukládány s využitím kódování UTF-8, což sice může znít překvapivě, ovšem přináší to i některé výhody. Autoři tohoto jazyka správně poukazují na to, že v současnosti prakticky všechny webové služby, XML soubory, JSON soubory, velká část HTML stránek atd. stejně již kódování UTF-8 standardně používají, takže nemá význam neustále provádět konverzi mezi tímto kódováním a například UCS-4 (UTF-32). Navíc je při zpracování rozsáhlých XML souborů formát UTF-8 výhodnější z hlediska spotřeby operační paměti. Největší nevýhodou použití UTF-8 je nemožnost získat a vrátit n-tý znak v řetězci v konstantním čase (a na některých architekturách též obtížná práce s jednotlivými bajty). Pokud by se operace pro získání n-tého znaku prováděla velmi často, lze samozřejmě použít vhodný objekt, který například „obaluje“ pole čtyřbajtových širokých znaků reprezentovaných v UCS-4/UTF-32.

V demonstračním příkladu popsaném v navazující kapitole bude použit řetězec obsahující znaky s českými akcenty, takže z výpisu bude patrné, jakým způsobem jsou tyto znaky reprezentovány i na straně céčkového programu (resp. na straně nativní knihovny).

2. Vypsání adresy ukazatele na straně Rustu i céčka

V předchozí části seriálu o Rustu jsme si řekli, že řetězce se do volaných nativních knihoven předávají přes ukazatel. To mj. znamená, že vlastní hodnota uložená v proměnné typu ukazatel na straně Rustu bude shodná s adresou uloženou v parametru typu char * či const char * na straně céčka. Toto tvrzení si můžeme snadno ověřit, protože adresu samozřejmě můžeme vypsat jak v céčkovém programu, tak i v té části, která je naprogramovaná v Rustu. Céčková část aplikace bude vypadat například takto (jak je patrné, stále budeme volat funkci vracející délku řetězce v bajtech):

#include <stdint.h>
 
#include <stdio.h>
 
int32_t string_length(const char *str)
{
    printf("C side pointer:    %p\n", str);
    int32_t len = 0;
    for (; *str; str++, len++)
        ;
    return len;
}

V té části aplikace, která je naprogramovaná v Rustu, získáme ukazatel s využitím metody CString.as_ptr() a ihned poté můžeme vypsat jeho hodnotu (tedy adresu):

fn test(string :&'static str) {
    match CString::new(string) {
        Ok(string) => {
            let pointer = string.as_ptr();
            println!("Rust side pointer: {:p}", pointer);
            unsafe {
                println!("string length = {}", string_length(pointer));
            }
        }
        Err(error) => {
            println!("CString::new() error: {:?}", error);
        }
    }
}

Pro zjištění, jak přesně se chovají znaky s akcenty v řetězcích, budeme funkci test() volat takto:

test("Hello world!");
test("");
test("ěščřžýáíé");

Celá rustovská část zdrojového kódu vypadá takto:

use std::ffi::CString;
use std::os::raw::c_char;
 
#[link(name = "ffi4")]
extern {
    fn string_length(str: *const c_char) -> i32;
}
 
fn test(string :&'static str) {
    match CString::new(string) {
        Ok(string) => {
            let pointer = string.as_ptr();
            println!("Rust side pointer: {:p}", pointer);
            unsafe {
                println!("string length = {}", string_length(pointer));
            }
        }
        Err(error) => {
            println!("CString::new() error: {:?}", error);
        }
    }
}
 
fn main() {
    test("Hello world!");
    test("");
    test("ěščřžýáíé");
}

Pro překlad obou částí aplikace a pro její následné spuštění opět použijeme pomocný soubor Makefile (ale níže si vysvětlíme lepší způsob spočívající ve využití projektu Cargo):

CC=gcc
CFLAGS=-Wall -ansi -fPIC
RUSTC=rustc
 
APP=ffi4
PROGNAME=$(APP)
LIBNAME=lib$(APP).so
 
RUST_SRC=$(APP).rs
LIB_SRC=$(APP).c
OBJ_FILE=$(APP).o
 
# Vychozi pravidlo pro vytvoreni vysledne spustitelne aplikace.
all:    $(LIBNAME) $(PROGNAME)
 
# Pravidlo pro slinkovani vsech objektovych souboru a vytvoreni
# vysledne spustitelne aplikace.
$(PROGNAME):    $(RUST_SRC) $(LIBNAME)
        $(RUSTC) -L . $<
 
# Pravidlo pro preklad knihovny
$(LIBNAME):     $(OBJ_FILE)
        $(CC) -shared -Wl,-soname,$(LIBNAME) -o $(LIBNAME) $<
 
%.o:    %.c
        $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
 
clean:
        rm -f *.o
        rm -f *.so
        rm -f $(PROGNAME)
 
run:
        export LD_LIBRARY_PATH=.;./$(APP)

Po překladu a spuštění tohoto příkladu můžeme získat například následující výstup. Konkrétní adresy řetězce se samozřejmě mohou odlišovat, obě však budou shodné (na straně Rustu i na straně nativní knihovny), protože se skutečně předává adresa řetězce a nikoli jeho obsah:

Rust side pointer: 0x2b8e67a26000
C side pointer:    0x2b8e67a26000
string length = 12
Rust side pointer: 0x2b8e67a1d020
C side pointer:    0x2b8e67a1d020
string length = 0
Rust side pointer: 0x2b8e67a1c0a0
C side pointer:    0x2b8e67a1c0a0
string length = 18

Povšimněte si, že vypočtená délka řetězce „ěščřžýáíé“ je osmnáct bajtů, zatímco samotný řetězec má jen devět znaků. Je to samozřejmě způsobeno použitým kódováním znaků. Malou úpravou příkladu lze kódy znaků snadno získat:

#include <stdio.h>
 
int32_t string_length(const char *str)
{
    printf("C side pointer:    %p\n", str);
    int32_t len = 0;
    for (; *str; str++, len++)
        printf("%02x ", (unsigned char)*str);
        ;
    return len;
}

S tímto výsledkem:

Rust side pointer: 0x2b3d2be26000
C side pointer:    0x2b3d2be26000
48 65 6c 6c 6f 20 77 6f 72 6c 64 21
string length = 12
 
Rust side pointer: 0x2b3d2be1d020
C side pointer:    0x2b3d2be1d020
string length = 0
 
Rust side pointer: 0x2b3d2be1c0a0
C side pointer:    0x2b3d2be1c0a0
c4 9b c5 a1 c4 8d c5 99 c5 be c3 bd c3 a1 c3 ad c3 a9
string length = 18

Poznámka: ve skutečnosti není příliš dobré používat standardní výstup společně na straně C a Rustu, protože se kvůli bufferování výstupu mohou řádky promíchat.

3. Předání adresy pole do funkce v nativní knihovně

Nyní si ukažme, jakým způsobem se do nativní funkce předá pole, konkrétně pole prvků typu f32. Již na začátku si však musíme uvědomit, že mezi poli v Rustu a céčku existuje mnoho rozdílů – pole v Rustu nejvíce odpovídají céčkovým polím s délkou známou překladači (vytvořenými přes []), zatímco dynamicky vytvořeným céčkovým polím ideově odpovídají řezy (slices), u nichž není délka známá v době překladu.

V předchozí části tohoto seriálu jsme si uvedli tabulku s převodem základních (primitivních) datových typů jazyka Rust a odpovídajících typů v céčku. Nyní si do této tabulky přidáme i informace o datových typech určených pro reprezentaci číselných hodnot reprezentovaných v systému plovoucí řádové čárky (floating point). V Rustu se jedná o typy f32 a f64, na straně céčka pak o typy float a double:

Typ v C Typ v Rustu Význam
int8_t i8 celé číslo se znaménkem (signed) o šířce 8 bitů
int16_t i16 celé číslo se znaménkem (signed) o šířce 16 bitů
int32_t i32 celé číslo se znaménkem (signed) o šířce 32 bitů
int64_t i64 celé číslo se znaménkem (signed) o šířce 64 bitů
     
uint8_t u8 celé číslo bez znaménka (unsigned) o šířce 8 bitů
uint16_t u16 celé číslo bez znaménka (unsigned) o šířce 16 bitů
uint32_t u32 celé číslo bez znaménka (unsigned) o šířce 32 bitů
uint64_t u64 celé číslo bez znaménka (unsigned) o šířce 64 bitů
     
float f32 numerická hodnota s plovoucí řádovou čárkou podle IEEE 754 s jednoduchou přesností
double f64 numerická hodnota s plovoucí řádovou čárkou podle IEEE 754 s dvojitou přesností

Pole prvků typu f32 budeme předávat, podobně jako tomu bylo u řetězců, přes ukazatel. Jelikož se bude předávat pouze „datová část“ pole bez dalších atributů, musíme všechny další informace nativní funkci předávat explicitně. Jedinou další požadovanou informací bude délka pole (u řetězců byla tato délka stanovena nepřímo vzdáleností bajtu s kódem nula od začátku řetězce). Námi vytvořená nativní funkce spočítá součet všech prvků pole; funkci budeme explicitně předávat délku i ukazatel na první prvek v poli:

#include <stdint.h>
 
#include <stdio.h>
 
float sum(uint32_t len, const float *array)
{
    float s = 0.0f;
    int i;
    for (i=0; i < len; i++) {
        s += array[i];
    }
    return s;
}

Poznámka: ve skutečnosti není vložení hlavičkového souboru stdio.h v tomto zdrojovém kódu nutné, pokud si ovšem opět nebudete chtít vypsat konkrétní předané parametry.

4. Druhý demonstrační příklad – výpočet sumy prvků pole

V rustovské části aplikace je nejprve nutné správně deklarovat hlavičku volané nativní funkce. Použijeme přitom typy std::os::raw::c_float a std::os::raw::c_uint:

#[link(name = "ffi5")]
extern {
    fn sum(len: c_uint, arr: *const c_float) -> c_float;
}

Dále pro dané pole získáme ukazatel s využitím metody as_ptr(), vypočteme délku pole metodou len() a tyto údaje předáme nativní funkci. Celá část, v níž se pracuje s ukazatelem, bude vložena do bloku unsafe:

let array : [f32; 5] = [1.0, 2.0, 3.0, 4.0, 5.0];
unsafe {
    let pointer = array.as_ptr();
    let len : u32 = array.len() as u32;
    let s = sum(len, pointer);
    println!("sum = {}", s);
}

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

use std::os::raw::c_float;
use std::os::raw::c_uint;
 
#[link(name = "ffi5")]
extern {
    fn sum(len: c_uint, arr: *const c_float) -> c_float;
}
 
fn main() {
    let array : [f32; 5] = [1.0, 2.0, 3.0, 4.0, 5.0];
    unsafe {
        let pointer = array.as_ptr();
        let len : u32 = array.len() as u32;
        let s = sum(len, pointer);
        println!("sum = {}", s);
    }
}

Po překladu a spuštění dostaneme lakonickou zprávu o součtu prvků pole:

sum = 15

5. Nedetekovaný problém při předání datové struktury přes ukazatel

Pokud se pokusíme přeložit tento program:

fn main() {
    let array : [f32; 5] = [1.0, 2.0, 3.0, 4.0, 5.0];
    for i in 1..6 {
        array[i] = 0.0;
    }
}

vypíše překladač chybové hlášení, protože se pokoušíme o zápis do pole, které je neměnitelné (immutable):

error: cannot assign to immutable indexed content `array[..]`
  --> ffi5.rs:12:9
   |
12 |         array[i] = 0.0;
   |         ^^^^^^^^^^^^^^
 
error: aborting due to previous error

Ve skutečnosti se ovšem jedná „pouze“ o omezení kladená překladačem. Pokud například upravíme nativní část aplikace tak, aby funkce kromě součtu prvků ještě prvky pole postupně vynulovala, nebude to překladačem zachyceno (ostatně do funkce se předává pouhý ukazatel):

#include <stdint.h>
 
#include <stdio.h>
 
float sum(uint32_t len, float *array)
{
    float s = 0.0f;
    int i;
    for (i=0; i < len; i++) {
        s += array[i];
        array[i] = 0.0f;
    }
    return s;
}

Rustovská část aplikace bude vypadat shodně s předchozím demonstračním příkladem:

use std::os::raw::c_float;
use std::os::raw::c_uint;
 
#[link(name = "ffi6")]
extern {
    fn sum(len: c_uint, arr: *const c_float) -> c_float;
}
 
fn main() {
    let array : [f32; 5] = [1.0, 2.0, 3.0, 4.0, 5.0];
    println!("array: {:?}", array);
    unsafe {
        let pointer = array.as_ptr();
        let len : u32 = array.len() as u32;
        let s = sum(len, pointer);
        println!("sum = {}", s);
    }
    println!("array: {:?}", array);
}

Pokud tento program spustíte, vypíše s velkou pravděpodobností následující výsledek:

array: [1, 2, 3, 4, 5]
sum = 15
array: [0, 0, 0, 0, 0]

Povšimněte si, že nativní funkce změnila hodnoty zapsané v poli, i když je pole zdánlivě neměnitelné! Navíc může nastat další problém – prvky pole mohou být uloženy v kódovém segmentu a při pokusu o jeho modifikaci dojde k pádu programu (což by bylo zcela korektní chování). Jinými slovy – při použití FFI ztrácíme poměrně velkou část výhod Rustu spočívající v přísných kontrolách prováděných překladačem.

6. Použití nástroje Cargo při práci s projekty používajícími nativní knihovny

Použití souborů Makefile nebo dokonce shellových skriptů určených pro překlad a slinkování aplikace složené z céčkové části a rustovské části je poměrně nešikovné a v praxi se s ním příliš často nesetkáme. Mnohem častěji se používá nástroj Cargo, který mj. podporuje i tvorbu vlastních build skriptů.

Zkusme si tedy nejprve vytvořit prázdný projekt spravovaný systémem Cargo. Projekt bude sloužit k vytvoření spustitelné binární aplikace a proto musíme použít přepínač –bin:

cargo new --bin ffi1

Po spuštění předchozího příkazu získáme následující adresářovou strukturu projektu:

.
├── Cargo.toml
└── src
    └── main.rs

Do projektu přesuneme soubor ffi1.c (s funkcí pro součet dvou celých čísel) a umístíme ho do podadresáře src. Navíc vytvoříme (například pomocí touch) soubor nazvaný build.rs, který bude umístěn ve stejném adresáři, v jakém se nachází projektový soubor Cargo.toml. Výsledek by měl vypadat následovně:

.
├── build.rs
├── Cargo.toml
└── src
    ├── ffi1.c
    └── main.rs

7. Nastavení projektu spravovaného nástrojem Cargo

Projektový soubor Cargo.toml v mém případě vypadal následovně (ve vašem případě se samozřejmě bude lišit řádek authors, ovšem to není v tuto chvíli příliš důležité):

[package]
name = "ffi_project1"
version = "0.1.0"
authors = ["Pavel Tisnovsky <ptisnovs@redhat.com>"]
 
[dependencies]

Do projektového souboru musíme přidat další direktivu, která říká, že se před překladem a před linkováním projektu má spustit i takzvaný build skript (ve skutečnosti to skript není, protože se jedná o zdrojový kód psaný v Rustu a překládaný do strojového kódu). Přidaný řádek je zvýrazněn:

[package]
name = "ffi_project1"
version = "0.1.0"
authors = ["Pavel Tisnovsky <ptisnovs@redhat.com>"]
build = "build.rs"
 
[dependencies]

Tento řádek má vliv jen na průběh překladu, nikoli na spouštění samotné aplikace.

8. Soubor build.rs

Samotný soubor build.rs vlastně představuje zdrojový kód běžného programu napsaného v Rustu, který ovšem přes svůj standardní výstup může předávat parametry překladači Rustu (typicky cesty ke knihovnám atd.). Úkolem tohoto souboru bude provést tři kroky, které jsme předtím implementovali přes soubor Makefile nebo jednoduchými shellovými skripty:

gcc -c src/ffi1.c -fPIC -o ffi1.o
 
ar rcsu libffi1.a ffi1.o

Připomeňme si, že z Rustu je možné relativně snadno spouštět nové procesy a předávat jim parametry. Právě tuto funkci nyní využijeme pro napodobení předchozího shell skriptu. Nejprve zjistíme jméno adresáře, kam se ukládají výsledné binární soubory (tento adresář může být odlišný podle typu překladu):

let out_dir = env::var("OUT_DIR").unwrap();

Následně spustíme překladač gcc a utilitu pro práci se statickými knihovnami ar, samozřejmě s předáním správných parametrů:

// preklad
Command::new("gcc").args(&["src/ffi1.c", "-c", "-fPIC", "-o"])
                   .arg(&format!("{}/ffi1.o", out_dir))
                   .status().unwrap();
 
// import objektoveho souboru do staticke knihovny
Command::new("ar").args(&["crus", "libffi1.a", "ffi1.o"])
                  .current_dir(&Path::new(&out_dir))
                  .status().unwrap();

Posledním krokem je předání parametrů překladači a linkeru. To se provádí poměrně elegantním způsobem přes standardní výstup – každý řádek vypisovaný aplikací build začínající textem „cargo:“ je zpracován, rozpoznán, převeden na správný přepínač a předán překladači a linkeru Rustu. Většina možných voleb je popsána na stránce http://doc.crates.io/build-script.html#outputs-of-the-build-script, nás však dnes bude zajímat prakticky jen volba rustc-link-lib=[KIND=]NAME, která je transformována do přepínače -l, s nímž jsme se již seznámili (za KIND lze doplnit static nebo dynlib) a volba ustc-link-search= transformovaná do přepínače -L (cesta ke knihovnám):

// predame prekladaci parametry
println!("cargo:rustc-link-search=native={}", out_dir);
println!("cargo:rustc-link-lib=static=ffi1");

Úplná podoba souboru build.rs je tedy následující:

// build.rs
 
use std::process::Command;
use std::env;
use std::path::Path;
 
fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
 
    // preklad
    Command::new("gcc").args(&["src/ffi1.c", "-c", "-fPIC", "-o"])
                       .arg(&format!("{}/ffi1.o", out_dir))
                       .status().unwrap();
 
    // import objektoveho souboru do staticke knihovny
    Command::new("ar").args(&["crus", "libffi1.a", "ffi1.o"])
                      .current_dir(&Path::new(&out_dir))
                      .status().unwrap();
 
    // predame prekladaci parametry
    println!("cargo:rustc-link-search=native={}", out_dir);
    println!("cargo:rustc-link-lib=static=ffi1");
}

9. Změny, které je nutné provést ve zdrojových kódech

Další změnu je zapotřebí provést ve zdrojových kódech. Netýká se to ovšem céčkové části, která zůstává stále stejná (ostatně zde není k žádné změně důvod):

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

V rustovské části aplikace je nutné odstranit řádek:

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

Pokud by tento řádek ve zdrojovém kódu zůstal, došlo by k pokusu o slinkování knihovny dvakrát, což by vedlo k chybě při kompilaci projektu:

$ cargo build
   Compiling ffi_project1 v0.1.0 (file:///home/tester/ffi_project1)
error: linking with `cc` failed: exit code: 1
  |
  = note: "cc" "-Wl,--as-needed" ...vynecháno...
  = note: /home/tester/ffi_project1/target/debug/build/ffi_project1-14178b3c9909eb85/out/libffi1.a(ffi1.o): In function `add':
ffi1.c:(.text+0x0): multiple definition of `add'
/home/tester/ffi_project1/target/debug/build/ffi_project1-14178b3c9909eb85/out/libffi1.a(ffi1.o):ffi1.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
 
 
error: aborting due to previous error
 
error: Could not compile `ffi_project1`.
 
To learn more, run the command again with --verbose.

Nová podoba rustovské části se tedy ve skutečnosti nepatrně zjednodušila:

extern {
    fn add(x:i32, y:i32) -> i32;
}
 
fn main() {
    let x:i32 = 1;
    let y:i32 = 2;
    let z = unsafe { add(x, y) };
    println!("{} + {} = {}", x, y, z);
}

10. Překlad a spuštění projektu

Po výše provedených úpravách je překlad a slinkování projektu velmi snadné a zajistí ho příkaz:

cargo build
   Compiling ffi_project1 v0.1.0 (file:///home/tester/ffi_project1)
    Finished debug [unoptimized + debuginfo] target(s) in 1.1 secs

Spuštění se provede stejně snadno:

cargo run
    Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/ffi_project1`
1 + 2 = 3

Překlad do finální podoby (s optimalizacemi atd.):

cargo build --release
   Compiling ffi_project1 v0.1.0 (file:///home/tester/ffi_project1)
    Finished release [optimized] target(s) in 1.10 secs

11. Zjednodušení skriptu pro překlad s použitím pluginu gcc

Build skript z předchozího projektu explicitně volal překladač céčka a utilitu ar, což je ovšem poměrně špatný přístup k řešení problému (prakticky stejně špatný jako naše soubory Makefile), a to z toho důvodu, že zbytečně explicitně určujeme, jaký překladač se má volat, jaké se mu mají předávat parametry atd. To ovšem vede k tvorbě potenciálně nepřenositelných zdrojových kódů. Mnohem lepší je použít modul (v systému Cargo se jim říká crate) http://alexcrichton.com/gcc-rs/gcc/index.html. Jméno tohoto modulu je mírně zavádějící, protože může podporovat i další typy překladačů. Vzhledem k tomu, že je tento modul používán pro sestavení aplikace, přidává se do sekce [build-dependencies] a nikoli [dependencies]:

[build-dependencies]
gcc = "0.3"

Do projektového souboru Cargo.toml je zapotřebí přidat dva zvýrazněné řádky:

name = "ffi_project2"
version = "0.1.0"
authors = ["Pavel Tisnovsky <ptisnovs@redhat.com>"]
build = "build.rs"
 
[dependencies]
 
[build-dependencies]
gcc = "0.3"

12. Upravený soubor build.rs

Musíme upravit i soubor build.rs, který se nyní zjednoduší, protože bude ve funkci main obsahovat jen jediný příkaz – volání funkce gcc::compile_library(). Tato funkce již zajistí vše ostatní, tedy i předání parametrů překladači a linkeru Rustu:

// balicek gcc zajisti preklad a vytvoreni knihovny za nas
extern crate gcc;
 
fn main() {
    // preklad a vytvoreni knihovny
    gcc::compile_library("libffi2.a", &["src/ffi2.c"]);
}

Překlad se nemění:

cargo build
   Compiling gcc v0.3.50
   Compiling ffi_project2 v0.1.0 (file:///home/tester/temp/presentations/rust/ffi/ffi_project2)
    Finished debug [unoptimized + debuginfo] target(s) in 3.42 secs

Spuštění bude taktéž stejné:

    Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/ffi_project2`
1 + 2 = 3

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

Všechny dnes popisované demonstrační příklady a podpůrné skripty byly, ostatně podobně jako ve všech předchozích částech tohoto seriálu, uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/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
ffi5.c https://github.com/tisnik/pre­sentations/blob/master/rus­t/ffi/ffi5/ffi5.c
ffi5.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/ffi/ffi5/ffi5.rs
make_binary.sh https://github.com/tisnik/pre­sentations/blob/master/rus­t/ffi/ffi5/make_binary.sh
make_library.sh https://github.com/tisnik/pre­sentations/blob/master/rus­t/ffi/ffi5/make_library.sh
Makefile https://github.com/tisnik/pre­sentations/blob/master/rus­t/ffi/ffi5/Makefile
   
ffi6.c https://github.com/tisnik/pre­sentations/blob/master/rus­t/ffi/ffi6/ffi6.c
ffi6.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/ffi/ffi6/ffi6.rs
make_binary.sh https://github.com/tisnik/pre­sentations/blob/master/rus­t/ffi/ffi6/make_binary.sh
make_library.sh https://github.com/tisnik/pre­sentations/blob/master/rus­t/ffi/ffi6/make_library.sh
Makefile https://github.com/tisnik/pre­sentations/blob/master/rus­t/ffi/ffi6/Makefile
   
Projekt1  
Cargo.toml https://github.com/tisnik/pre­sentations/blob/master/rus­t/ffi/ffi_project1/Cargo.toml
build.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/ffi/ffi_project1/build.rs
ffi1.c https://github.com/tisnik/pre­sentations/blob/master/rus­t/ffi/ffi_project1/src/ffi1­.c
main.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/ffi/ffi_project1/src/ma­in.rs
   
Projekt2  
Cargo.toml https://github.com/tisnik/pre­sentations/blob/master/rus­t/ffi/ffi_project2/Cargo.toml
build.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/ffi/ffi_project2/build.rs
ffi2.c https://github.com/tisnik/pre­sentations/blob/master/rus­t/ffi/ffi_project2/src/ffi2­.c
main.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/ffi/ffi_project2/src/ma­in.rs

14. Odkazy na Internetu

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

Autor článku

Pavel Tišnovský vystudoval VUT FIT a v současné době pracuje ve společnosti Red Hat, kde vyvíjí nástroje pro OpenShift.io.