Hlavní navigace

Programovací jazyk Rust: binární halda, použití TCP

13. 4. 2017
Doba čtení: 18 minut

Sdílet

 Autor: Rust project
Poslední datovou strukturou ve standardní knihovně jazyka Rust je takzvaná binární halda, kterou si dnes podrobněji popíšeme. V závěru článku se navíc zmíníme o podpoře TCP v základní knihovně Rustu.

Obsah

1. Datová struktura BinaryHeap (binární halda)

2. Interní implementace binární haldy

3. Časová složitost základních operací s binární haldou

4. Triviální příklad použití binární haldy

5. Problematika uspořádání prvků

6. Iterátor pro BinaryHeap versus funkce pop()

7. Převod prvků z binární haldy do vektoru

8. Převod prvků do setříděného vektoru

9. Postupný přesun prvků z binární haldy s použitím funkce drain()

10. Podpora TCP v základní knihovně programovacího jazyka Rust

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

12. Odkazy na Internetu

1. Datová struktura BinaryHeap (binární halda)

Poslední datovou strukturou, která je ve standardní knihovně programovacího jazyka Rust zařazena mezi datové kolekce, je takzvaná binární halda neboli binary heap. Interně je binární halda, jakožto konkrétní implementace strukturovaného datového typu haldy (viz poznámka níže), reprezentována speciálně uloženým binárním stromem, na nějž jsou vztažena další dvě omezení:

  1. Až na poslední úroveň musí být binární strom vyvážený. Poslední úroveň může být buď kompletní (potom je pochopitelně celý binární strom vyvážený) nebo nekompletní. V případě, že je poslední úroveň nekompletní, naplňují se uzly na této úrovni zleva doprava.
  2. Hodnota uložená v každém uzlu takového binárního stromu musí být větší nebo rovna hodnotám uloženým v potomcích uzlu. Naproti tomu tato vlastnost nijak přesněji nespecifikuje, jak (a zda vůbec) musí být potomci daného uzlu uspořádáni. Pokud například oba potomky nějakého uzlu prohodíme, jedná se o zcela korektní operaci, která nijak toto pravidlo (omezení) neporuší.

Samozřejmě platí, že podmínka větší nebo rovna je volitelná, přesněji řečeno si programátor sám může zvolit funkci, která tuto podmínku testuje. Pro tento účel se používá trait str::cmp::Ord (https://doc.rust-lang.org/std/cmp/trait.Ord.html).

Poznámka: pojem halda (heap) má v kontextu programovacího jazyka Rust minimálně dva významy. Označuje se jím jak datová struktura popsaná výše, tak i ta část operační paměti, která je využívána pro alokaci dynamické paměti (resp. dynamických proměnných).

Poznámka2: halda jakožto datová struktura musí splňovat takzvanou vlastnost haldy: pokud je B potomek A, pak platí x(A) >= x(B). To znamená, že v kořenu stromu je vždy umístěn prvek s nejvyšším klíčem.

2. Interní implementace binární haldy

Existuje několik způsobů implementace binární haldy. Samozřejmě se skutečně může jednat o klasický binární strom, tj. o datovou strukturu obsahující uzly, přičemž každý uzel obsahuje ukazatele na své dva potomky. Ovšem v praxi se setkáme spíše s implementací binární haldy pomocí pole nebo v případě programovacího jazyka Rust s využitím vektoru. To je umožněno díky prvnímu požadavku na binární strom reprezentující haldu: až na poslední úroveň musí být binární strom vyvážený. Tato alternativní reprezentace, v níž není nutné používat ukazatele ani reference, s sebou samozřejmě přináší mnohé výhody, především pak menší nároky na operační paměť, rychlejší přístup k prvkům haldy a lepší využití L1 i L2 cache (což je možná dnes zdaleka nejdůležitější). Konkrétně v Rustu je reprezentace binární haldy následující:

pub struct BinaryHeap<T> {
    data: Vec<T>,
}

Poznámka: počet uzlů se zjistí jednoduše použitím metody len() pro vektor.

Jak však reprezentace binární haldy pomocí pole nebo vektoru vypadá? První prvek v poli či vektoru obsahuje kořen, tj. prvek s maximální hodnotou (podle druhé podmínky z předchozí kapitoly). Následující dva prvky pole/vektoru obsahují přímé potomky kořenového prvku. Další maximálně čtyři prvky pole/vektoru obsahují potomky předchozích dvou uzlů (potomků kořene) a tak dále až do dosažení potřebného počtu prvků. Lze snadno zjistit, že pro prvek uložený v poli/vektoru na indexu n budou jeho potomci uloženi na pozici 2n+1 a 2n+2. Pro kořen s indexem n=0 jsou jeho potomci skutečně uloženi na pozicích 2×0+1=1 a 2×0+2=2. První z těchto uzlů s indexem/pozicí n=1 má potomky na pozicích 2×1+1=3 a 2×1+2=4 atd. Podívejme se ostatně na ilustrační obrázek:

Obrázek 1: Interní reprezentace haldy.
Zdroj: Wikipedia

Poznámka: právě z tohoto způsobu uložení plyne nutnost naplňovat poslední úroveň stromu zleva doprava.

3. Časová složitost základních operací s binární haldou

Nejdůležitější praktickou vlastností binární haldy je zjištění prvku s maximální hodnotou v konstantním čase, protože tento prvek musí být uložen v kořenu stromu. Mj. i z tohoto důvodu se binární haldy používají pro implementaci prioritních front a podobných užitečných struktur. V následující tabulce jsou vypsány časové složitosti dalších často používaných operací. Nejhorší složitost má operace pro sloučení dvou binárních hald, zatímco vložení či vyjmutí prvku je operace se „slušnou“ logaritmickou složitostí (je tomu tak z toho důvodu, že log n odpovídá výšce stromu):

Operace Časová složitost
Stavba haldy O(n)
Získání hodnoty kořene O(1)
Vyjmutí kořene z haldy O(log n)
Vložení prvku do haldy O(log n)
Odstranění prvku z haldy O(log n)
Sloučení 2 hald O(n1 + n2)

Algoritmus pro vkládání prvku do binární haldy je popsán na Wikipedii.

4. Triviální příklad použití binární haldy

Do binární haldy je možné ukládat jakékoli datové typy implementující již výše zmíněný trait Ord. To se týká i řetězců, takže se podívejme na velmi jednoduchý příklad, v němž do binární haldy vložíme pětici řetězců (ve skutečnosti se vkládají reference na konstantní řetězce). Povšimněte si, že podobně jako u dalších typů kolekcí, si typ prvků odvodí překladač automaticky po první operaci heap.push(). Pro jednoduchost prvky z haldy získáváme s využitím iterátoru:

use std::collections::BinaryHeap;
 
fn main() {
    let mut heap = BinaryHeap::new();
 
    heap.push("Trachta");
    heap.push("Hlavacek");
    heap.push("Bierhanzel");
    heap.push("Meyer");
    heap.push("Vaclav Kotek");
 
    for item in &heap {
        println!("{}", item);
    }
}

Po spuštění tohoto příkladu získáme následující výstup. Povšimněte si, že pořadí prvků je (pseudo)náhodné:

Vaclav Kotek
Trachta
Bierhanzel
Hlavacek
Meyer

5. Problematika uspořádání prvků

V předchozí části tohoto seriálu zazněla otázka, jak by bylo možné uspořádat prvky představující komplexní čísla. Pokud totiž budeme chtít komplexní čísla vložit do binární haldy, je vyžadována implementace traitu Ord. Jedno z možných (špatných :-) řešení může vypadat následovně. Implementujeme traity Ord a PartialOrd (povšimněte si, jaké datové typy vrací metody cmp a partial_cmp):

impl Ord for Complex {
    fn cmp(&self, other: &Complex) -> Ordering {
        self.real.cmp(&other.real)
    }
}
 
impl PartialOrd for Complex {
    fn partial_cmp(&self, other: &Complex) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

Volání self.real.cmp() je možné z toho důvodu, že tato metoda (a trait Cmp) pro celá čísla existuje (pro čísla s plovoucí řádovou čárkou však nikoli):

#[derive(Clone, Debug)]
struct Complex {
    real: i32,
    imag: i32,
}

Navíc ještě implementujeme trait Eq a PartialEq:

impl PartialEq for Complex {
    fn eq(&self, other: &Complex) -> bool {
        self.real == other.real
    }
}
 
impl Eq for Complex {}

Program, který vloží několik komplexních čísel do binární haldy, může vypadat následovně:

use std::collections::BinaryHeap;
use std::cmp::Ordering;
 
#[derive(Clone, Debug)]
struct Complex {
    real: i32,
    imag: i32,
}
 
impl Complex {
    fn new(real: i32, imag: i32) -> Complex {
        Complex{real:real, imag:imag}
    }
    fn print(&self) {
        println!("complex number: {:}+{:}i", self.real, self.imag);
    }
}
 
impl Ord for Complex {
    fn cmp(&self, other: &Complex) -> Ordering {
        self.real.cmp(&other.real)
    }
}
 
impl PartialOrd for Complex {
    fn partial_cmp(&self, other: &Complex) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}
 
impl PartialEq for Complex {
    fn eq(&self, other: &Complex) -> bool {
        self.real == other.real
    }
}
 
impl Eq for Complex {}
 
impl Drop for Complex {
    fn drop(&mut self) {
        println!("Dropping complex number: {:}+{:}i", self.real, self.imag);
    }
}
 
fn main() {
    let mut heap = BinaryHeap::new();
 
    heap.push(Complex::new(0, 0));
    heap.push(Complex::new(1, 1));
    heap.push(Complex::new(0, 0));
    heap.push(Complex::new(1, 1));
    heap.push(Complex::new(2, 2));
 
    println!("max: {:?}", heap.peek());
    for item in &heap {
        item.print();
    }
}

Povšimněte si, že zavoláním heap.peek() můžeme získat maximální prvek v binární haldě, tj. vlastně kořen stromu:

max: Some(Complex { real: 2, imag: 2 })
complex number: 2+2i
complex number: 1+1i
complex number: 0+0i
complex number: 0+0i
complex number: 1+1i
Dropping complex number: 2+2i
Dropping complex number: 1+1i
Dropping complex number: 0+0i
Dropping complex number: 0+0i
Dropping complex number: 1+1i

Komplexní čísla jsou opět vrácena v obecně náhodném pořadí.

6. Iterátor pro BinaryHeap versus funkce pop()

Zkusme nyní do binární haldy ukládat celá čísla, pro něž je uspořádání triviální. Nejprve do haldy vložíme devět hodnot v zadaném pořadí, následně získáme prvek s maximální hodnotou, posléze vypíšeme prvky v pořadí vráceném iterátorem a na závěr použijeme metodu pop() pro postupné čtení prvků z haldy:

use std::collections::BinaryHeap;
 
fn main() {
    let mut heap = BinaryHeap::new();
 
    heap.push(1);
    heap.push(2);
    heap.push(3);
    heap.push(4);
    heap.push(100);
    heap.push(5);
    heap.push(6);
    heap.push(7);
    heap.push(8);
 
    println!("max value: {:?}", heap.peek());
 
    for item in &heap {
        println!("{}", item);
    }
 
    for _ in 0..heap.len()+1 {
        println!("{:?}", heap.pop());
    }
 
}

Podívejme se na výstup tohoto programu. Maximální prvek je vrácen jako hodnota typu Option (None/Some), následně přes iterátor získáme prvky v obecně náhodném pořadí a metoda pop() postupně vrátí prvky setříděné podle jejich velikosti. Pokud se dojde na konec haldy, vrátí se hodnota None (nedojde však k žádné výjimce):

max value: Some(100)
100
8
6
7
3
2
5
1
4
Some(100)
Some(8)
Some(7)
Some(6)
Some(5)
Some(4)
Some(3)
Some(2)
Some(1)
None

7. Převod prvků z binární haldy do vektoru

Binární halda implementuje metodu nazvanou into_vec(), která dokáže převést haldu na vektor. Vzhledem k tomu, že již víme, že halda je implementována vektorem, je tato operace pravděpodobně provedena velmi rychle:

use std::collections::BinaryHeap;
 
fn main() {
    let mut heap = BinaryHeap::new();
 
    heap.push(1);
    heap.push(2);
    heap.push(3);
    heap.push(4);
    heap.push(100);
    heap.push(5);
    heap.push(6);
    heap.push(7);
    heap.push(8);
 
    println!("max value: {:?}", heap.peek());
 
    let vec = heap.into_vec();
 
    for item in &vec {
        println!("{}", item);
    }
}

Prvky výsledného vektoru jsou obecně vráceny v náhodném pořadí. Přesněji řečeno není pořadí zcela náhodné, protože první prvek bude mít maximální hodnotu, ale další pořadí již není pevně dané:

max value: Some(100)
100
8
6
7
3
2
5
1
4

8. Převod prvků do setříděného vektoru

Mnohem častěji budeme vyžadovat převod binární haldy do vektoru, ovšem takovým způsobem, aby byly jeho prvky vzestupně setříděny. K tomuto účelu dobře poslouží metoda nazvaná into_sorted_vec(), jejíž implementace však není tak triviální, jako tomu bylo u metody into_vec():

use std::collections::BinaryHeap;
 
fn main() {
    let mut heap = BinaryHeap::new();
 
    heap.push(1);
    heap.push(2);
    heap.push(3);
    heap.push(4);
    heap.push(100);
    heap.push(5);
    heap.push(6);
    heap.push(7);
    heap.push(8);
 
    println!("max value: {:?}", heap.peek());
 
    let vec = heap.into_sorted_vec();
 
    for item in &vec {
        println!("{}", item);
    }
}

Z výsledného výpisu je patrné, že prvky jsou skutečně vráceny setříděné:

max value: Some(100)
1
2
3
4
5
6
7
8
100

9. Postupný přesun prvků z binární haldy s použitím funkce drain()

Již minule jsme se zmiňovali o funkci pojmenované drain(), díky níž je možné postupně zpracovávat prvky z datových kolekcí bez nutnosti jejich klonování. Tato funkce samozřejmě existuje i pro binární haldu, takže se jen rychle podívejme na příklad jejího použití:

use std::collections::BinaryHeap;
 
fn main() {
    let mut heap = BinaryHeap::new();
 
    heap.push(1);
    heap.push(2);
    heap.push(3);
    heap.push(4);
    heap.push(100);
    heap.push(5);
    heap.push(6);
    heap.push(7);
    heap.push(8);
 
    println!("max value: {:?}", heap.peek());
 
    let iter = heap.drain();
 
    for item in iter {
        println!("{}", item);
    }
}

Výsledek bude shodný s použitím metody iter(), protože prvky budou zpracovány v tom pořadí, v jakém jsou uloženy do vektoru:

max value: Some(100)
100
8
6
7
3
2
5
1
4

10. Podpora TCP v základní knihovně programovacího jazyka Rust

Ve standardní knihovně programovacího jazyka Rust nalezneme i podporu pro práci s TCP. To mj. znamená, že je možné relativně snadno tvořit aplikace běžící v režimu serveru (s nasloucháním na zvoleném portu či portech) či v režimu klienta (který se připojuje k serveru). Základními datovými strukturami, které se v tomto případě používají, jsou struktury TcpListener a TcpStream. Názvy těchto struktur napovídají, jak se používají – TcpListener slouží pro implementaci jednoduchého serveru, TcpStream pak představuje objekt zajišťující obousměrný přenos dat mezi klientem a serverem.

Podívejme se nyní na příklad velmi jednoduchého serveru, který naslouchá na portu 1234 a který realizuje odpovědi v jediném vláknu (což může být samozřejmě omezující). Číslo portu je větší než 1024, takže ho může otevřít i aplikace bez rootovských práv. Listener se vytvoří triviálně (zejména v porovnání s tím, co by se dělo v céčku):

let listener = TcpListener::bind("127.0.0.1:1234").unwrap();

Následně se získá nekonečný iterátor, který při každém požadavku o spojení od klienta vrátí strukturu typu Option<Result<TcpStream>>, která již reprezentuje obousměrný komunikační kanál. Vzhledem k tomu, že je iterátor nekonečný, bude nutné server ukončit jinak, například přes Ctrl+C z terminálu, posláním signálu STOP příkazem kill atd.:

let tcp_stream_iter = listener.incoming();

Způsob realizace na požadavek o spojení může vypadat takto:

for tcp_stream in tcp_stream_iter {
    if tcp_stream.is_ok() {
        handler(tcp_stream.unwrap());
    } else {
        println!("connection failed");
    }
}

Handler je prozatím velmi primitivní, protože pouze klientovi pošle textovou odpověď a následně spojení ukončí:

fn handler(mut stream:TcpStream) {
    println!("Accepted connection");
    stream.write(b"Server response...\r\n").unwrap();
}

Celý zdrojový kód vypadá následovně:

use std::io::Write;
use std::net::TcpListener;
use std::net::TcpStream;
 
fn handler(mut stream:TcpStream) {
    println!("Accepted connection");
    stream.write(b"Server response...\r\n").unwrap();
}
 
fn main() {
    let listener = TcpListener::bind("127.0.0.1:1234").unwrap();
 
    let tcp_stream_iter = listener.incoming();
 
    for tcp_stream in tcp_stream_iter {
        if tcp_stream.is_ok() {
            handler(tcp_stream.unwrap());
        } else {
            println!("connection failed");
        }
    }
}

Nejprve spustíme server:

$ ./292_tcp_listener_1

Následně se z jiného terminálu pokusíme k serveru připojit. Měli bychom přijmout zprávu „Server response…“ a spojení by se mělo automaticky ukončit:

$ telnet localhost 1234
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Server response...
Connection closed by foreign host.

Vylepšení tohoto serveru si ukážeme příště.

root_podpora

11. 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ář:

Již dopředu se můžete podívat na demonstrační příklady, s nimiž se seznámíme v navazující části tohoto seriálu:

12. Odkazy na Internetu

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

Byl pro vás článek přínosný?