Hlavní navigace

Reakce na chyby v programovacím jazyku Rust

Pavel Tišnovský

V každém programu je nutné adekvátně reagovat na různé typy chyb. Programovací jazyk Rust nabízí dvě standardní metody reakcí na chyby: použití výjimek a oznámení o chybě s využitím návratové hodnoty.

Obsah

1. Reakce na chyby v programovacím jazyku Rust

2. Když nelze vrátit žádnou rozumnou hodnotu: datový typ Option

3. Oznámení o chybě formou návratové hodnoty: datový typ Result

4. Příklad použití datového typu Result

5. Predikáty is_ok() a is_err()

6. Datový typ Result a pattern matching

7. Kontrola, zda jsou pokryty obě větve v konstrukci match

8. Znak „_“ použitý v konstrukci match

9. Funkce vyššího řádu map

10. Funkce vyššího řádu map_err

11. Více variant chybových hlášení

12. Použití výčtu pro chybová hlášení

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

14. Odkazy na Internetu

1. Reakce na chyby v programovacím jazyku Rust

V předchozí části seriálu o programovacím jazyku Rust jsme se seznámili s užitečným datovým typem Option, který se velmi často používá v těch situacích, v nichž je zapotřebí vrátit (či naopak předat či alespoň reprezentovat) nějakou speciální hodnotu. Příklad použití jsme si vysvětlili na velmi jednoduché funkci, která vydělí dvě celá čísla a vrátí celočíselný podíl „zabalený“ právě v datovém typu Option. Ovšem ve chvíli, kdy je druhé číslo nulové, vrací se namísto zabaleného výsledku speciální hodnota None, takže volající funkce může a někdy dokonce musí kdykoli zjistit, že namísto skutečného podílu získala tuto speciální hodnotu.

2. Když nelze vrátit žádnou rozumnou hodnotu: datový typ Option

Funkce pro celočíselné dělení vypadala následovně:

fn div(x: i32, y: i32) -> Option<i32> {
    if y != 0 {
        Some(x/y)
    } else {
        None
    }
}

Jednou z největších předností datového typu Option je fakt, že jeho používání je v programovacím jazyku Rust do značné míry standardní a navíc idiomatické, takže programátoři nemusí hledat, která „magická konstanta“ je pro danou funkci použita. Dále je zaručeno, že pokud budeme chtít získat zabalenou hodnotu přes pattern matching, bude nutné explicitně použít i druhou větev pracující s None. Pro hodnoty typu Option je navíc možné volat různé více či méně užitečné metody, například is_none(), is_some(), expect(), unwrap(), and_then(), or_else() a různé varianty funkce vyššího řádu map(). Mimochodem – tato struktura se používá i v případě, že potřebujeme pracovat s referencemi, které v některých situacích nemusí existovat (což nám jinak Rust nedovolí).

3. Oznámení o chybě formou návratové hodnoty: datový typ Result

V mnoha případech však nemusí být použití datového typu Option tím nejlepším řešením. Pro příklad nemusíme chodit daleko – předpokládejme, že budeme chtít, aby naše funkce pro dělení celých čísel vracela v případě pokusu o dělení nulou chybové hlášení a nikoli nicneříkající hodnotu None. K tomuto účelu se v programovacím jazyku Rust používá datová struktura nazvaná příhodně Result. Tato datová struktura se podobá Option, ovšem s tím rozdílem, že obaluje buď výsledek (třeba návratovou hodnotu volané funkce) nebo informaci o chybě. Deklarace struktury Result vypadá následovně:

enum Result<T, E> {
    Ok(T),
    Err(E),
}

což se liší od deklarace typu Option:

enum Option<T> {
    None,
    Some(T),
}

Povšimněte si, že se u datové struktury Result specifikují dva typy – typ návratové hodnoty T a typ hodnoty reprezentující chybu E. To je poměrně užitečná vlastnost, protože se programátoři mohou sami rozhodnout, jakým způsobem budou reprezentovat chybu – zda se bude jednat o jednoduchý řetězec či o složitější datovou strukturu, v ní může být například uloženo jméno otevíraného souboru a současně chybové hlášení systému při pokusu o jeho otevření.

4. Příklad použití datového typu Result

Zkusme si nyní upravit naši funkci určenou pro dělení dvou celých čísel takovým způsobem, aby se v případě dělení nulou namísto hodnoty None vracelo plnohodnotné chybové hlášení ve formě řetězce. Úprava je velmi snadná a může vypadat následovně:

fn div(x: i32, y: i32) -> Result<i32, &'static str> {
    if y != 0 {
        Ok(x/y)
    } else {
        Err("Divide by zero!")
    }
}

Prozatím si vypočtené hodnoty vytiskneme jednoduše makrem println!() a formátovacím příkazem „?:“:

fn main() {
    let z1 = div(2, 1);
    println!("{:?}", z1);
 
    let z2 = div(2, 0);
    println!("{:?}", z2);
}

Po spuštění tohoto příkladu se na prvním řádku vypíše vypočtená hodnota obalená do „Ok“ a na řádku druhém pak chybové hlášení, tentokrát obalené do „Err“:

Ok(2)
Err("Divide by zero!")

Poznámka: v tomto případě se do Err uloží reference na statický řetězec, který je neměnitelný a který je součástí výsledného spustitelného binárního souboru. Konkrétně se tento řetězec nachází v sekci nazvané .rodata, která má nastavená práva Exec a Read, nikoli však Write (ostatně i proto se jedná o neměnitelný řetězec).

5. Predikáty is_ok() a is_err()

Zatímco u struktury Option bylo možné zjistit typ uložené hodnoty, tedy rozlišit mezi Some a None, pomocí predikátů is_some() a is_none(), používají se u datové struktury Result predikáty nazvané is_ok() a is_err(). Jejich použití je velmi jednoduché, jak je ostatně ukázáno na dalších dvou příkladech.

Použití predikátu is_ok()

fn div(x: i32, y: i32) -> Result<i32, &'static str> {
    if y != 0 {
        Ok(x/y)
    } else {
        Err("Divide by zero!")
    }
}
 
fn print_div_result(result: Result<i32, &'static str>) {
    if result.is_ok() {
        println!("Result is correct");
    } else {
        println!("Result is incorrect");
    }
}
 
fn main() {
    let z1 = div(2, 1);
    print_div_result(z1);
 
    let z2 = div(2, 0);
    print_div_result(z2);
}

Po spuštění tohoto příkladu se vypíše:

Result is correct
Result is incorrect

Použití predikátu is_err()

fn div(x: i32, y: i32) -> Result<i32, &'static str> {
    if y != 0 {
        Ok(x/y)
    } else {
        Err("Divide by zero!")
    }
}
 
fn print_div_result(result: Result<i32, &'static str>) {
    if result.is_err() {
        println!("Result is incorrect");
    } else {
        println!("Result is correct");
    }
}
 
fn main() {
    let z1 = div(2, 1);
    print_div_result(z1);
 
    let z2 = div(2, 0);
    print_div_result(z2);
}

Po spuštění tohoto příkladu se vypíše:

Result is correct
Result is incorrect

6. Datový typ Result a pattern matching

Ve skutečnosti se často namísto predikátů a čtení zabalené hodnoty či chybové zprávy používá pattern matching. Další příklad se nápadně podobá příkladu, který již známe z předchozí části seriálu:

fn div(x: i32, y: i32) -> Result<i32, &'static str> {
    if y != 0 {
        Ok(x/y)
    } else {
        Err("Divide by zero!")
    }
}
 
fn print_div_result(result: Result<i32, &'static str>) {
    match result {
        Ok(value)  => println!("value: {}", value),
        Err(error) => println!("error: {}", error)
    }
}
 
fn main() {
    let z1 = div(2, 1);
    print_div_result(z1);
 
    let z2 = div(2, 0);
    print_div_result(z2);
}

Vidíme, že je díky pattern matchingu v konstrukci match možné získat jak správnou hodnotu (pokud existuje), tak i hodnotu reprezentující chybu.

Výsledek běhu tohoto příkladu:

value: 2
error: Divide by zero!

Pro připomenutí a porovnání si ukažme, jak byl pattern matching využit společně s datovým typem Option:

fn div(x: i32, y: i32) -> Option<i32> {
    if y != 0 {
        Some(x/y)
    } else {
        None
    }
}
 
fn div_and_print(x: i32, y :i32) {
    let result = div(x, y);
    println!("{:?}", result);
 
    match result {
        None      => println!("Divide by zero"),
        Some(val) => println!("{} / {} = {}", x, y, val),
    }
 
    println!("");
}
 
fn main() {
    div_and_print(2, 1);
    div_and_print(2, 0);
}

7. Kontrola, zda jsou pokryty obě větve v konstrukci match

O tom, že při pattern matchingu je v konstrukci match nutné pokrýt všechny možnosti (větve) jsme se již několikrát zmiňovali, takže si jen rychle zkontrolujme, zda to platí i pro strukturu Result. V následujícím příkladu naschvál vynecháme jednu z větví a zkontrolujeme průběh překladu:

fn div(x: i32, y: i32) -> Result<i32, &'static str> {
    if y != 0 {
        Ok(x/y)
    } else {
        Err("Divide by zero!")
    }
}
 
fn print_div_result1(result: Result<i32, &'static str>) {
    match result {
        //Ok(value)  => println!("value: {}", value),
        Err(error) => println!("error: {}", error)
    }
}
 
fn print_div_result2(result: Result<i32, &'static str>) {
    match result {
        Ok(value)  => println!("value: {}", value),
        //Err(error) => println!("error: {}", error)
    }
}
 
fn main() {
    let z1 = div(2, 1);
    print_div_result1(z1);
 
    let z2 = div(2, 0);
    print_div_result2(z2);
}

Překladač podle očekávání obě chyby snadno nalezne a vypíše přehledná chybová hlášení:

error[E0004]: non-exhaustive patterns: `Ok(_)` not covered
  --> 215_result_patern_matching_coverage.rs:10:11
   |
10 |     match result {
   |           ^^^^^^ pattern `Ok(_)` not covered
 
error[E0004]: non-exhaustive patterns: `Err(_)` not covered
  --> 215_result_patern_matching_coverage.rs:17:11
   |
17 |     match result {
   |           ^^^^^^ pattern `Err(_)` not covered
 
error: aborting due to 2 previous errors

8. Znak „_“ použitý v konstrukci match

V konstrukci match je možné namísto vzoru použít i znak „_“, kterým je možné implementovat obdobu větve default známou z céčkových jazyků, ale i z Javy či JavaScriptu (konstrukce switch/case). Pokud tedy například nepotřebujeme získat přesnou informaci o chybě, která vznikla, můžeme náš skript upravit následujícím způsobem:

fn div(x: i32, y: i32) -> Result<i32, &'static str> {
    if y != 0 {
        Ok(x/y)
    } else {
        Err("Divide by zero!")
    }
}
 
fn print_div_result(result: Result<i32, &'static str>) {
    match result {
        Ok(value)  => println!("value: {}", value),
        _          => println!("some error")
    }
}
 
fn main() {
    let z1 = div(2, 1);
    print_div_result(z1);
 
    let z2 = div(2, 0);
    print_div_result(z2);
}

Výsledek běhu tohoto příkladu:

value: 2
some error

Pokud se vám pattern matching zalíbil (skutečně se jedná o velmi silnou část Rustu), je možné upravit i funkci div, například následovně:

fn div(x: i32, y: i32) -> Result<i32, &'static str> {
    match y {
        0 => Err("Divide by zero!"),
        _ => Ok(x / y)
    }
}
 
fn print_div_result(result: Result<i32, &'static str>) {
    match result {
        Ok(value)  => println!("value: {}", value),
        _          => println!("some error")
    }
}
 
fn main() {
    let z1 = div(2, 1);
    print_div_result(z1);
 
    let z2 = div(2, 0);
    print_div_result(z2);
}

9. Funkce vyššího řádu map

Ve všech předchozích demonstračních příkladech jsme museli se strukturou Result pracovat dosti nešikovným způsobem – při každém přístupu bylo nutné kontrolovat, jestli je výsledek korektní (Ok) nebo chybový (Err), což by však v reálných programech bylo dosti nečitelné. Ve skutečnosti nám však struktura Result nabízí i mnohé další možnosti. Velmi užitečná je funkce map, která se aplikuje na výsledek, ovšem jen ve chvíli, kdy je výsledek korektní (Ok). Díky tomu se obejdeme bez nutnosti použití rozhodovací konstrukce či pattern matchingu. V následujícím příkladu vypíšeme výsledek dělení i výsledek zvýšený o jedničku. Povšimněte si, že do funkce map předáváme funkci akceptující celé číslo a vracející taktéž celé číslo (o rozbalení a zabalení výsledků se nemusíme vůbec starat):

fn div(x: i32, y: i32) -> Result<i32, &'static str> {
    if y != 0 {
        Ok(x/y)
    } else {
        Err("Divide by zero!")
    }
}
 
fn print_div_result(result: Result<i32, &'static str>) {
    match result {
        Ok(value)  => println!("value: {}", value),
        Err(error) => println!("error: {}", error)
    }
}
 
fn inc(x: i32) -> i32 {
    x+1
}
 
fn main() {
    let z0 = div(0, 1);
    print_div_result(z0);
    print_div_result(z0.map(inc));
 
    let z1 = div(2, 1);
    print_div_result(z1);
    print_div_result(z1.map(inc));
 
    let z2 = div(2, 0);
    print_div_result(z2);
    print_div_result(z2.map(inc));
}

Program bude plně funkční, a to i když použijeme map(inc) na výsledek dělení nulou:

value: 0
value: 1
value: 2
value: 3
error: Divide by zero!
error: Divide by zero!

10. Funkce vyššího řádu map_err

Obdobou funkce map je funkce map_err. Asi jste již uhodli, že tato funkce vyššího řádu bude použita ve chvíli, kdy je ve struktuře Result uložena chybová hodnota, nikoli hodnota korektní. V následujícím příkladu budeme přes map_err překládat chybová hlášení do češtiny (tento příklad je poněkud umělý). Základem bude funkce translate transformující anglické řetězce:

fn div(x: i32, y: i32) -> Result<i32, &'static str> {
    if y != 0 {
        Ok(x/y)
    } else {
        Err("Divide by zero!")
    }
}
 
fn print_div_result(result: Result<i32, &'static str>) {
    match result {
        Ok(value)  => println!("value: {}", value),
        Err(error) => println!("error: {}", error)
    }
}
 
fn translate(s: &'static str) -> &'static str {
    if s == "Divide by zero!" {
        "Deleni nulou"
    } else {
        "Neznama chyba"
    }
}
 
fn main() {
    let z0 = div(0, 1);
    print_div_result(z0);
    print_div_result(z0.map_err(translate));
 
    let z1 = div(2, 1);
    print_div_result(z1);
    print_div_result(z1.map_err(translate));
 
    let z2 = div(2, 0);
    print_div_result(z2);
    print_div_result(z2.map_err(translate));
}

Výsledek běhu tohoto programu:

value: 0
value: 0
value: 2
value: 2
error: Divide by zero!
error: Deleni nulou

11. Více variant chybových hlášení

Díky tomu, že ve struktuře Result může být chybová hodnota reprezentována libovolným datovým typem (na rozdíl od Option), je možné pracovat s větším množstvím chybových hlášení. Můžeme si představit funkci pro otevření souboru, načtení jeho obsahu a následné zavření souboru – zde může dojít k celé řadě různých chyb (soubor nelze otevřít, lze ho otevřít, ale ne přečíst, při čtení může dojít k chybě/odpojení disku, soubor nemusí jít zavřít atd. atd.). Zkusme si nepatrně upravit naši funkci pro dělení tak, aby rozpoznala speciální případ 0/0. Úprava se dotkne i funkce translate:

fn div(x: i32, y: i32) -> Result<i32, &'static str> {
    if y != 0 {
        Ok(x/y)
    } else if x != 0 {
        Err("Divide by zero!")
    } else {
        Err("Zero/zero!")
    }
}
 
fn print_div_result(result: Result<i32, &'static str>) {
    match result {
        Ok(value)  => println!("value: {}", value),
        Err(error) => println!("error: {}", error)
    }
}
 
fn translate(s: &'static str) -> &'static str {
    match s {
         "Divide by zero!" => "Deleni nulou",
         "Zero/zero!"      => "Nula delena nulou",
         _                 => "Neznama chyba"
    }
}
 
fn main() {
    let z0 = div(0, 1);
    print_div_result(z0);
    print_div_result(z0.map_err(translate));
 
    let z1 = div(2, 1);
    print_div_result(z1);
    print_div_result(z1.map_err(translate));
 
    let z2 = div(2, 0);
    print_div_result(z2);
    print_div_result(z2.map_err(translate));
 
    let z3 = div(0, 0);
    print_div_result(z3);
    print_div_result(z3.map_err(translate));
}

Podívejme se na výsledek spuštění tohoto programu:

value: 0
value: 0
value: 2
value: 2
error: Divide by zero!
error: Deleni nulou
error: Zero/zero!
error: Nula delena nulou

12. Použití výčtu pro chybová hlášení

Velmi často se můžeme setkat s programy, v nichž se pro reprezentaci chybové hodnoty používá namísto pouhých řetězců výčet (enum). Předností je snadnější práce s hodnotami typu výčet a možnost tisku jmen jednotlivých prvků výčtu v případě, že se použije #[derive(Debug)]. Ostatně podívejme se na příklad, v němž nejprve deklarujeme výčtový typ s dvojicí prvků (hodnot) a následně ho použijeme pro reprezentaci chybových stavů. Povšimněte si, že k jednotlivým prvkům/hodnotám se přistupuje s využitím operátoru „čtyřtečky“:

#[derive(Debug)]
enum DivError {
    DivideByZero,
    DivideZeroByZero
}
 
fn div(x: i32, y: i32) -> Result<i32, DivError> {
    if y != 0 {
        Ok(x/y)
    } else if x != 0 {
        Err(DivError::DivideByZero)
    } else {
        Err(DivError::DivideZeroByZero)
    }
}
 
fn print_div_result(result: Result<i32, DivError>) {
    match result {
        Ok(value)  => println!("value: {}", value),
        Err(error) => println!("error: {:?}", error)
    }
}
 
fn main() {
    let z0 = div(0, 1);
    print_div_result(z0);
 
    let z1 = div(2, 1);
    print_div_result(z1);
 
    let z2 = div(2, 0);
    print_div_result(z2);
 
    let z3 = div(0, 0);
    print_div_result(z3);
}

Výsledek běhu tohoto programu:

value: 0
value: 2
error: DivideByZero
error: DivideZeroByZero

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

Všechny dnes popisované demonstrační příklady byly, 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. Příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý repositář:

14. Odkazy na Internetu

  1. Enum std::result::Result
    https://doc.rust-lang.org/std/result/enum.Result.html
  2. Module std::result
    https://doc.rust-lang.org/std/result/
  3. Result
    http://rustbyexample.com/std/re­sult.html
  4. Rust stdlib: Option
    https://doc.rust-lang.org/std/option/enum.Option.html
  5. Module std::option
    https://doc.rust-lang.org/std/option/index.html
  6. Rust by example: option
    http://rustbyexample.com/std/op­tion.html
  7. Rust by example: if-let
    http://rustbyexample.com/flow_con­trol/if_let.html
  8. Rust by example: while let
    http://rustbyexample.com/flow_con­trol/while_let.html
  9. Rust by example: Option<i32>
    http://rustbyexample.com/std/op­tion.html
  10. An Overview of Macros in Rust
    http://words.steveklabnik.com/an-overview-of-macros-in-rust
  11. A Practical Intro to Macros in Rust 1.0
    https://danielkeep.github.io/practical-intro-to-macros.html
  12. The Rust Programming Language: macros
    https://doc.rust-lang.org/beta/book/macros.html
  13. Rust by example: 15 macro_rules!
    http://rustbyexample.com/macros.html
  14. Module std::vec
    https://doc.rust-lang.org/nightly/std/vec/index.html
  15. Primitive Type isize
    https://doc.rust-lang.org/nightly/std/primi­tive.isize.html
  16. Primitive Type usize
    https://doc.rust-lang.org/nightly/std/primi­tive.usize.html
  17. Primitive Type array
    https://doc.rust-lang.org/nightly/std/primi­tive.array.html
  18. Module std::slice
    https://doc.rust-lang.org/nightly/std/slice/
  19. Rust by Example: 2.3 Arrays and Slices
    http://rustbyexample.com/pri­mitives/array.html
  20. What is the difference between Slice and Array (stackoverflow)
    http://stackoverflow.com/qu­estions/30794235/what-is-the-difference-between-slice-and-array
  21. Learning Rust With Entirely Too Many Linked Lists
    http://cglab.ca/~abeinges/blah/too-many-lists/book/
  22. Testcase: linked list
    http://rustbyexample.com/cus­tom_types/enum/testcase_lin­ked_list.html
  23. Operators and Overloading
    https://doc.rust-lang.org/book/operators-and-overloading.html
  24. Module std::ops
    https://doc.rust-lang.org/std/ops/index.html
  25. Module std::cmp
    https://doc.rust-lang.org/std/cmp/index.html
  26. Trait std::ops::Add
    https://doc.rust-lang.org/stable/std/ops/trait.Add.html
  27. Trait std::ops::AddAssign
    https://doc.rust-lang.org/std/ops/trait.AddAssign.html
  28. Trait std::ops::Drop
    https://doc.rust-lang.org/std/ops/trait.Drop.html
  29. Trait std::cmp::Eq
    https://doc.rust-lang.org/std/cmp/trait.Eq.html
  30. Struct std::boxed::Box
    https://doc.rust-lang.org/std/boxed/struct.Box.html
  31. Explore the ownership system in Rust
    https://nercury.github.io/rus­t/guide/2015/01/19/ownership­.html
  32. Rust's ownership and move semantic
    http://www.slideshare.net/sa­neyuki/rusts-ownership-and-move-semantics
  33. Trait std::marker::Copy
    https://doc.rust-lang.org/stable/std/marker/tra­it.Copy.html
  34. Trait std::clone::Clone
    https://doc.rust-lang.org/stable/std/clone/tra­it.Clone.html
  35. The Stack and the Heap
    https://doc.rust-lang.org/book/the-stack-and-the-heap.html
  36. Rust Compare: Pointers & References
    http://www.rust-compare.com/site/pointers.html
  37. Rust Compare: Parameters
    http://www.rust-compare.com/site/params.html
  38. Why does this compile? Automatic dereferencing?
    https://users.rust-lang.org/t/why-does-this-compile-automatic-dereferencing/2183
  39. Understanding Pointers, Ownership, and Lifetimes in Rust
    http://koerbitz.me/posts/Understanding-Pointers-Ownership-and-Lifetimes-in-Rust.html
  40. Rust lang series episode #25 — pointers (#rust-series)
    https://steemit.com/rust-series/@jimmco/rust-lang-series-episode-25-pointers-rust-series
  41. Rust – home page
    https://www.rust-lang.org/en-US/
  42. Rust – Frequently Asked Questions
    https://www.rust-lang.org/en-US/faq.html
  43. Destructuring and Pattern Matching
    https://pzol.github.io/get­ting_rusty/posts/20140417_des­tructuring_in_rust/
  44. The Rust Programming Language
    https://doc.rust-lang.org/book/
  45. Rust (programming language)
    https://en.wikipedia.org/wi­ki/Rust_%28programming_lan­guage%29
  46. Go – home page
    https://golang.org/
  47. Stack Overflow – Most Loved, Dreaded, and Wanted language
    https://stackoverflow.com/re­search/developer-survey-2016#technology-most-loved-dreaded-and-wanted
  48. 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/
  49. Rust vs Go: My experience
    https://www.reddit.com/r/go­lang/comments/21m6jq/rust_vs_go_my_ex­perience/
  50. Friends of Rust (Organizations running Rust in production)
    https://www.rust-lang.org/en-US/friends.html
  51. Rust programs versus C++ g++
    https://benchmarksgame.ali­oth.debian.org/u64q/compa­re.php?lang=rust&lang2=gpp
  52. Další benchmarky (nejedná se o reálné příklady „ze života“)
    https://github.com/kostya/benchmarks
  53. Go na Redditu
    https://www.reddit.com/r/golang/
  54. Rust vs. Go
    http://vschart.com/compare/rust/vs/go-language
  55. Abstraction without overhead: traits in Rust
    https://blog.rust-lang.org/2015/05/11/traits.html
  56. Method Syntax
    https://doc.rust-lang.org/book/method-syntax.html
  57. Traits in Rust
    https://doc.rust-lang.org/book/traits.html
  58. Functional Programming in Rust – Part 1 : Function Abstraction
    http://blog.madhukaraphatak­.com/functional-programming-in-rust-part-1/
  59. 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
  60. Chytré ukazatele (moderní verze jazyka C++) [MSDN]
    https://msdn.microsoft.com/cs-cz/library/hh279674.aspx
  61. UTF-8 Everywhere
    http://utf8everywhere.org/
  62. Rust by Example
    http://rustbyexample.com/
  63. Rust oficiálně ve Fedoře
    https://mojefedora.cz/rust-oficialne-ve-fedore/
  64. Resource acquisition is initialization
    https://en.wikipedia.org/wi­ki/Resource_acquisition_is_i­nitialization
  65. TIOBE index (October 2016)
    http://www.tiobe.com/tiobe-index/
  66. 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
  67. String Types in Rust
    http://www.suspectsemantic­s.com/blog/2016/03/27/str­ing-types-in-rust/
  68. Trait (computer programming)
    https://en.wikipedia.org/wi­ki/Trait_%28computer_program­ming%29
  69. Type inference
    https://en.wikipedia.org/wi­ki/Type_inference
Našli jste v článku chybu?