Obsah
1. Přístup ke složkám datové struktury přes ukazatel
2. Změna složek datové struktury ve chvíli, kdy již byla odstraněna z paměti
4. Ukazatelová aritmetika: metoda offset()
5. Přístup mimo prvky pole přes ukazatel
6. Zápis do paměti přes ukazatel
8. Pokus o přístup do paměti přes ukazatel NULL
10. Chování aplikace při přímém přiřazení nové hodnoty do proměnné
11. Chování aplikace při použití operace std::ptr::write()
13. Repositář s demonstračními příklady
1. Přístup ke složkám datové struktury přes ukazatel
Důležité upozornění: tento článek se, podobně jako článek předchozí, zabývá relativně okrajovou částí programovacího jazyka Rust. V praxi byste se s použitím ukazatelů v Rustu neměli setkat příliš často, a to především z toho důvodu, že mnohé vlastnosti ukazatelů jsou nahrazeny jinými prostředky – referencemi, „řezy“ (slice), typem „Box“, typem „Rc“ atd. Na druhou stranu je ovšem dobré znát i temné a nebezpečné zákoutí programovacího jazyka.
Vraťme se k demonstračnímu příkladu, který jsme si ukázali na konci předchozího článku. V tomto příkladu je deklarován datový typ (struktura) Complex, k níž existuje i destruktor. Následně vytvoříme hodnotu tohoto typu a adresu, na níž je hodnota uložena, předáme do ukazatele. V příkladu je dále ukázáno, že k prvkům datové struktury je možné přistupovat i přes ukazatel: (*pointer).real. Přitom je nutné mít na paměti, že v Rustu neexistuje obdoba operátoru – > známého z C a C++:
#[derive(Debug)] struct Complex { real: f32, imag: f32, } impl Complex { fn new(real: f32, imag: f32) -> Complex { println!("Constructing complex number: {:}+{:}i", real, imag); Complex{real:real, imag:imag} } } impl Drop for Complex { fn drop(&mut self) { println!("Dropping complex number: {:}+{:}i", self.real, self.imag); } } fn main() { let mut value: Complex = Complex::new(1.0, 2.0); let pointer: *mut Complex; pointer = &mut value; println!("{:?}", value); unsafe { println!("{:?}", *pointer); } value.real = 10.0; value.imag = 20.0; println!("{:?}", value); unsafe { println!("{:?}", *pointer); } unsafe { (*pointer).real = 20.0; (*pointer).imag = 40.0; } println!("{:?}", value); unsafe { println!("{:?}", *pointer); } }
Podívejme se, jaké zprávy se vypíšou při spuštění tohoto příkladu:
Constructing complex number: 1+2i Complex { real: 1, imag: 2 } Complex { real: 1, imag: 2 } Complex { real: 10, imag: 20 } Complex { real: 10, imag: 20 } Complex { real: 20, imag: 40 } Complex { real: 20, imag: 40 } Dropping complex number: 20+40i
2. Změna složek datové struktury ve chvíli, kdy již byla odstraněna z paměti
Při použití ukazatelů překladač programovacího jazyka Rust předpokládá, že programátor přesně ví, co dělá, takže se neprovádí prakticky žádné kontroly životnosti/viditelnost proměnných. V následujícím příkladu má proměnná value omezenou živostnost na „svůj“ blok {}, zatímco ukazatel nazvaný pointer k proměnné přistupuje i ve chvíli, kdy již byla opuštěna oblast její platnosti (to bude patrné po spuštění aplikace):
#[derive(Debug)] struct Complex { real: f32, imag: f32, } impl Complex { fn new(real: f32, imag: f32) -> Complex { println!("Constructing complex number: {:}+{:}i", real, imag); Complex{real:real, imag:imag} } } impl Drop for Complex { fn drop(&mut self) { println!("Dropping complex number: {:}+{:}i", self.real, self.imag); } } fn main() { let pointer: *mut Complex; { let mut value: Complex = Complex::new(1.0, 2.0); pointer = &mut value; println!("{:?}", value); unsafe { println!("{:?}", *pointer); } value.real = 10.0; value.imag = 20.0; println!("{:?}", value); unsafe { println!("{:?}", *pointer); } unsafe { (*pointer).real = 20.0; (*pointer).imag = 40.0; } println!("{:?}", value); unsafe { println!("{:?}", *pointer); } } // !!! unsafe { (*pointer).real = -1.1; (*pointer).imag = -2.2; println!("{:?}", *pointer); } }
Velmi zajímavé věci nastanou při spuštění aplikace. Povšimněte si především toho, že skutečně vypršela oblast viditelnosti proměnné typu Complex a dokonce byl zavolán její destruktor. Teprve poté jsme přes ukazatel zapsali do paměti, která ovšem již proměnné nepatří! V tomto konkrétním případě sice nedošlo k pádu aplikace (což souvisí se způsobem alokace zásobníkových rámců), ale samozřejmě se jedná o sémanticky špatně napsaný program:
Constructing complex number: 1+2i Complex { real: 1, imag: 2 } Complex { real: 1, imag: 2 } Complex { real: 10, imag: 20 } Complex { real: 10, imag: 20 } Complex { real: 20, imag: 40 } Complex { real: 20, imag: 40 } Dropping complex number: 20+40i Complex { real: -1.1, imag: -2.2 }
Představte si navíc tento (sémanticky taktéž chybný) příklad, který opět půjde přeložit:
fn get_ptr_to_complex() -> *mut Complex { let mut value: Complex = Complex::new(1.0, 2.0); println!("{:?}", value); return &mut value; }
3. Použití metody as_ptr()
U některých typů je možné získat ukazatel na hodnotu daného typu pomocí metody as_ptr(). Z datových typů, s nimiž se často setkáte, se jedná především o řetězce (str) a řezy poli (slice). Vzhledem k tomu, že v naprosté většině případů dokáže překladač Rustu automaticky volat metody pro slice přímo nad zdrojovými poli (pro které je řez vytvořen), je možné metodu as_ptr() volat i pro celé pole:
let array: [i32; 5] = [1, 2, 3, 4, 5]; let pointer: *const i32 = array.as_ptr(); println!("{:?}", array);
Poznámka: ve skutečnosti je řez/slice interně reprezentován právě ukazatelem + počtem prvků.
4. Ukazatelová aritmetika: metoda offset()
Oproti programovacímu jazyku C nejsou v Rustu některé operace s ukazateli prováděny přes „klasickou“ ukazatelovou aritmetiku (přičtení offsetu k ukazateli přetíženým operátorem + atd.), ovšem namísto toho je možné použít metodu offset(), která vypočte offset na základě datového typu ukazatele – jinými slovy pokud se jedná o ukazatel na hodnotu typu i8 nebo pole i8, bude offet použit přímo, u typu i32 bude offset vynásoben čtyřmi, protože jeden prvek i32 zabírá v paměti čtyři bajty. Podívejme se na jednoduchý příklad, v němž se vytvoří pole s pěti prvky typu i32 a následně se přistoupí k prvnímu prvku přímo přes ukazatel, dále ke stejnému prvku přes offset (nulový) a poté ke druhému prvku pole:
fn main() { let array: [i32; 5] = [1, 2, 3, 4, 5]; let pointer: *const i32 = array.as_ptr(); println!("{:?}", array); unsafe { println!("{}", *pointer); println!("{}", *pointer.offset(0)); println!("{}", *pointer.offset(1)); } }
Po překladu a spuštění by se mělo vypsat:
[1, 2, 3, 4, 5] 1 1 2
Typ ukazatele se odvodí automaticky, takže ho nemusíme explicitně zapisovat:
fn main() { let array: [i32; 5] = [1, 2, 3, 4, 5]; let pointer = array.as_ptr(); println!("{:?}", array); unsafe { println!("{}", *pointer); println!("{}", *pointer.offset(0)); println!("{}", *pointer.offset(1)); } }
Ukazatel ovšem můžeme získat například i pro druhý prvek pole, čímž se situace změní, protože offsety budou počítány relativně k tomuto prvku. Zde můžeme použít záporného offsetu k přístupu do předchozího prvku:
fn main() { let array: [i32; 5] = [1, 2, 3, 4, 5]; let pointer: *const i32 = &array[1] as *const i32; println!("{:?}", array); unsafe { println!("{}", *pointer); println!("{}", *pointer.offset(-1)); println!("{}", *pointer.offset(1)); } }
Po překladu a spuštění by se mělo vypsat:
[1, 2, 3, 4, 5] 2 1 3
5. Přístup mimo prvky pole přes ukazatel
Pravděpodobně jste si povšimli, že metodu offset() voláme v bloku unsafe. To je vyžadováno překladačem z toho prostého důvodu, že není (a obecně ani nemůže být) prováděna žádná kontrola, jestli přes offset stále přistupujeme k platným adresám či zda je offset zadán špatně. V následujícím programu hned na třech místech přistupujeme do paměti, která nám vlastně nepatří:
fn main() { let array: [i32; 5] = [1, 2, 3, 4, 5]; let pointer: *const i32 = &array[0] as *const i32; println!("{:?}", array); unsafe { println!("{}", *pointer); println!("{}", *pointer.offset(1)); // !!! println!("{}", *pointer.offset(10)); // !!! println!("{}", *pointer.offset(-1)); // !!! println!("{}", *pointer.offset(-10)); } }
V mém případě se program podařilo spustit bez segfaultu (což však není zaručeno), ovšem samozřejmě přečteme pseudonáhodné hodnoty:
[1, 2, 3, 4, 5] 1 2 32678 1808587265 0
6. Zápis do paměti přes ukazatel
Přes metodu offset() získáme vlastně jen jiný ukazatel, takže můžeme provádět i nepřímý zápis. V následujícím příkladu se nejdříve vypíšou původní hodnoty pětiprvkového pole, následně se dva prvky změní, a to nepřímo přes ukazatel a posléze se vypíše nový obsah pole. Samozřejmě i zápis přes ukazatel a výpočet offsetu musí být umístěn v bloku unsafe:
fn main() { let mut array: [i32; 5] = [1, 2, 3, 4, 5]; let pointer: *mut i32 = array.as_mut_ptr(); println!("{:?}", array); unsafe { *pointer = 100; *pointer.offset(2) = 200; } println!("{:?}", array); }
Příklad výstupu:
[1, 2, 3, 4, 5] [100, 2, 200, 4, 5]
7. Ukazatel NULL
V programovacím jazyku Rust je možné pracovat i s ukazatelem obsahujícím speciální hodnotu NULL. Ta se vytváří jednoduše – funkcí ptr::null() pro ukazatele na konstantní hodnoty popř. funkcí ptr::null_mut() pro ukazatele na hodnoty měnitelné. Použití těchto funkcí je snadné:
use std::ptr; fn main() { let pointer: *const i32 = ptr::null(); println!("{:p}", pointer); }
Příklad výpisu asi skalní céčkaře nepřekvapí:
0x0
Inicializace ukazatele na měnitelnou hodnotu:
use std::ptr; fn main() { let pointer: *mut i32 = ptr::null_mut(); println!("{:p}", pointer); }
Příklad výpisu (skutečně se zde jedná o stejnou adresu), ovšem její typ je odlišný:
0x0
Pokud budeme chtít ukazatel sice inicializovat na hodnotu NULL, ale později adresu změnit, musíme deklarovat měnitelnou (mutable) proměnnou:
use std::ptr; fn main() { let mut pointer: *const i32 = ptr::null(); println!("{:p}", pointer); let value: i32 = 42; pointer = &value; println!("{:p}", pointer); }
Příklad výpisu:
0x0 0x7fff2aab8c1c
8. Pokus o přístup do paměti přes ukazatel NULL
Přístup do paměti přes ukazatel nastavený na NULL je sice možný – překladač takovou konstrukci nebude detekovat ani ji nebude hlásit jako chybu – ale na všech moderních 32bitových a 64bitových architekturách vždy dojde k pádu aplikace v runtime (což je asi jediné rozumné chování). Otestování je snadné.
Čtení:
use std::ptr; fn main() { let pointer: *const i32 = ptr::null(); println!("{:p}", pointer); unsafe { println!("{}", *pointer); } }
Jedna z mála možností, jak v Rustu dosáhnout segfaultu:
$ ./test 0x0 Segmentation fault $ echo $? 139
Zápis:
use std::ptr; fn main() { let pointer: *mut i32 = ptr::null_mut(); println!("{:p}", pointer); unsafe { *pointer = 42; } }
Nahlásí se stejná chyba:
$ ./test 0x0 Segmentation fault $ echo $? 139
9. Operace std::ptr::write()
Další vyloženě nebezpečnou operací je operace představována funkcí std::ptr::write(). Tato funkce provádí zdánlivě banální operaci – zápis nové hodnoty na zadanou adresu:
use std::ptr; fn main() { let mut x = 0; let pointer: *mut i32 = &mut x; let y = 42; println!("{:p}", pointer); println!("{}", x); unsafe { ptr::write(pointer, y); } println!("{}", x); }
Ve skutečnosti se však provede jen přepis staré hodnoty hodnotou novou, ale již se neprovede korektní zrušení staré hodnoty: nezavolá se destruktor atd. Proto je nutné tuto operaci použít skutečně jen ve chvíli, kdy víme, co děláme.
10. Chování aplikace při přímém přiřazení nové hodnoty do proměnné
Podívejme se nyní, jak se aplikace chová ve chvíli, kdy do proměnné přiřadíme jinou hodnotu (k tomu se vůbec nepoužívají ukazatele):
use std::ptr; #[derive(Debug)] struct Complex { real: f32, imag: f32, } impl Complex { fn new(real: f32, imag: f32) -> Complex { println!("Constructing complex number: {:}+{:}i", real, imag); Complex{real:real, imag:imag} } } impl Drop for Complex { fn drop(&mut self) { println!("Dropping complex number: {:}+{:}i", self.real, self.imag); } } fn main() { let mut value: Complex = Complex::new(1.0, 2.0); let value2: Complex = Complex::new(100.0, 200.0); let pointer: *mut Complex = &mut value; println!("{:p}", pointer); println!("{:?}", value); value = value2; println!("{:p}", pointer); println!("{:?}", value); }
Podle očekávání získáme následující výsledek – jsou zkonstruována dvě komplexní čísla a tudíž se taktéž zavolají dva destruktory ve chvíli, kdy končí životnost hodnot:
Constructing complex number: 1+2i Constructing complex number: 100+200i 0x7fffe54b4658 Complex { real: 1, imag: 2 } Dropping complex number: 1+2i 0x7fffe54b4658 Complex { real: 100, imag: 200 } Dropping complex number: 100+200i
Podobně tomu bude ve chvíli, kdy upravíme funkci main() tak, aby se přiřazení provedlo přes ukazatel:
fn main() { let mut value: Complex = Complex::new(1.0, 2.0); let value2: Complex = Complex::new(100.0, 200.0); let pointer: *mut Complex = &mut value; println!("{:p}", pointer); println!("{:?}", value); unsafe { *pointer = value2; } println!("{:p}", pointer); println!("{:?}", value); }
Překladač Rustu je v tomto případě dost chytrý na to, aby poznal, že přiřazením v bloku unsafe končí životnost prvního komplexního čísla.
11. Chování aplikace při použití operace std::ptr::write()
Třetí modifikace příkladu spočívá v použití std::ptr::write() namísto přiřazení:
use std::ptr; #[derive(Debug)] struct Complex { real: f32, imag: f32, } impl Complex { fn new(real: f32, imag: f32) -> Complex { println!("Constructing complex number: {:}+{:}i", real, imag); Complex{real:real, imag:imag} } } impl Drop for Complex { fn drop(&mut self) { println!("Dropping complex number: {:}+{:}i", self.real, self.imag); } } fn main() { let mut value: Complex = Complex::new(1.0, 2.0); let value2: Complex = Complex::new(100.0, 200.0); let pointer: *mut Complex = &mut value; println!("{:p}", pointer); println!("{:?}", value); unsafe { ptr::write(pointer, value2); } println!("{:p}", pointer); println!("{:?}", value); }
Zde je již chování diametrálně odlišné, neboť se nezavolá destruktor pro první komplexní číslo!
Constructing complex number: 1+2i Constructing complex number: 100+200i 0x7fff05ac3478 Complex { real: 1, imag: 2 } 0x7fff05ac3478 Complex { real: 100, imag: 200 } Dropping complex number: 100+200i
Tuto operaci má tedy smysl použít jen v několika ojedinělých případech, kdy přesně víme, že nechceme volat destruktory staré hodnoty.
12. Kdy nepoužít ukazatele?
Jak již bylo naznačeno v úvodní kapitole, používají se ukazatele v Rustu jen v relativně malém množství případů, typicky při nutnosti volání nativních funkcí přes FFI či při implementaci efektivních datových struktur (viz zdrojové kódy pro Vec). Důležitější je pro céčkaře vědět, kdy naopak ukazatele nepoužít, protože Rust nabízí lepší (typicky bezpečnější) řešení:
- Pokud potřebujete předat parametr do funkce odkazem a ne hodnotou, použijte referenci.
- Pokud potřebujete alokovat strukturu na haldě, použijte typ Box. Rust si sám ohlídá viditelnost/životnost struktury.
- Pokud navíc potřebujete automatickou správu paměti s počítáním referencí, je k dispozici typ Rc a Arc.
- Někdy se v C používají ukazatele proto, aby se mohla vrátit „žádná hodnota“ (tedy NULL) nebo „chyba“ (většinou taktéž NULL). V takovém případe je mnohem lepší použít typ Option nebo Result (liší se především sémantikou)
13. Repositář s demonstračními příklady
Všechny dnes popisované demonstrační příklady 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/presentations. Demonstrační příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý repositář:
14. Odkazy na Internetu
- The Rustonomicon: The Dark Arts of Advanced and Unsafe Rust Programming
https://doc.rust-lang.org/nomicon/ - Primitive Type pointer
https://doc.rust-lang.org/std/primitive.pointer.html - Cargo: správce projektů a balíčků pro programovací jazyk Rust
https://mojefedora.cz/cargo-spravce-projektu-a-balicku-pro-programovaci-jazyk-rust/ - Network Communication and Serialization in Rust
https://www.safaribooksonline.com/blog/2014/01/28/network-communication-serialization-rust/ - Crate bincode
http://tyoverby.com/bincode/bincode/index.html - Struct std::fs::File
https://doc.rust-lang.org/std/fs/struct.File.html - Trait std::io::Seek
https://doc.rust-lang.org/std/io/trait.Seek.html - Trait std::io::Read
https://doc.rust-lang.org/std/io/trait.Read.html - Trait std::io::Write
https://doc.rust-lang.org/std/io/trait.Write.html - Trait std::io::BufRead
https://doc.rust-lang.org/std/io/trait.BufRead.html - Module std::io::prelude
https://doc.rust-lang.org/std/io/prelude/index.html - std::net::IpAddr
https://doc.rust-lang.org/std/net/enum.IpAddr.html - std::net::Ipv4Addr
https://doc.rust-lang.org/std/net/struct.Ipv4Addr.html - std::net::Ipv6Addr
https://doc.rust-lang.org/std/net/struct.Ipv6Addr.html - TcpListener
https://doc.rust-lang.org/std/net/struct.TcpListener.html - TcpStream
https://doc.rust-lang.org/std/net/struct.TcpStream.html - Binary heap (Wikipedia)
https://en.wikipedia.org/wiki/Binary_heap - Binární halda (Wikipedia)
https://cs.wikipedia.org/wiki/Bin%C3%A1rn%C3%AD_halda - Halda (datová struktura)
https://cs.wikipedia.org/wiki/Halda_%28datov%C3%A1_struktura%29 - Struct std::collections::HashSet
https://doc.rust-lang.org/std/collections/struct.HashSet.html - Struct std::collections::BTreeSet
https://doc.rust-lang.org/std/collections/struct.BTreeSet.html - Struct std::collections::BinaryHeap
https://doc.rust-lang.org/std/collections/struct.BinaryHeap.html - Set (abstract data type)
https://en.wikipedia.org/wiki/Set_%28abstract_data_type%29#Language_support - Associative array
https://en.wikipedia.org/wiki/Associative_array - Hash Table
https://en.wikipedia.org/wiki/Hash_table - B-tree
https://en.wikipedia.org/wiki/B-tree - Pedro Celis: Robin Hood Hashing (naskenované PDF!)
https://cs.uwaterloo.ca/research/tr/1986/CS-86–14.pdf - Robin Hood hashing
http://codecapsule.com/2013/11/11/robin-hood-hashing/ - Robin Hood hashing: backward shift deletion
http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/ - Module std::collections
https://doc.rust-lang.org/std/collections/ - Module std::vec
https://doc.rust-lang.org/nightly/std/vec/index.html - Struct std::collections::VecDeque
https://doc.rust-lang.org/std/collections/struct.VecDeque.html - Struct std::collections::LinkedList
https://doc.rust-lang.org/std/collections/struct.LinkedList.html - Module std::fmt
https://doc.rust-lang.org/std/fmt/ - Macro std::println
https://doc.rust-lang.org/std/macro.println.html - Enum std::result::Result
https://doc.rust-lang.org/std/result/enum.Result.html - Module std::result
https://doc.rust-lang.org/std/result/ - Result
http://rustbyexample.com/std/result.html - Rust stdlib: Option
https://doc.rust-lang.org/std/option/enum.Option.html - Module std::option
https://doc.rust-lang.org/std/option/index.html - Rust by example: option
http://rustbyexample.com/std/option.html - Rust by example: if-let
http://rustbyexample.com/flow_control/if_let.html - Rust by example: while let
http://rustbyexample.com/flow_control/while_let.html - Rust by example: Option<i32>
http://rustbyexample.com/std/option.html - An Overview of Macros in Rust
http://words.steveklabnik.com/an-overview-of-macros-in-rust - A Practical Intro to Macros in Rust 1.0
https://danielkeep.github.io/practical-intro-to-macros.html - The Rust Programming Language: macros
https://doc.rust-lang.org/beta/book/macros.html - Rust by example: 15 macro_rules!
http://rustbyexample.com/macros.html - Primitive Type isize
https://doc.rust-lang.org/nightly/std/primitive.isize.html - Primitive Type usize
https://doc.rust-lang.org/nightly/std/primitive.usize.html - Primitive Type array
https://doc.rust-lang.org/nightly/std/primitive.array.html - Module std::slice
https://doc.rust-lang.org/nightly/std/slice/ - Rust by Example: 2.3 Arrays and Slices
http://rustbyexample.com/primitives/array.html - What is the difference between Slice and Array (stackoverflow)
http://stackoverflow.com/questions/30794235/what-is-the-difference-between-slice-and-array - Learning Rust With Entirely Too Many Linked Lists
http://cglab.ca/~abeinges/blah/too-many-lists/book/ - Testcase: linked list
http://rustbyexample.com/custom_types/enum/testcase_linked_list.html - Operators and Overloading
https://doc.rust-lang.org/book/operators-and-overloading.html - Module std::ops
https://doc.rust-lang.org/std/ops/index.html - Module std::cmp
https://doc.rust-lang.org/std/cmp/index.html - Trait std::ops::Add
https://doc.rust-lang.org/stable/std/ops/trait.Add.html - Trait std::ops::AddAssign
https://doc.rust-lang.org/std/ops/trait.AddAssign.html - Trait std::ops::Drop
https://doc.rust-lang.org/std/ops/trait.Drop.html - Trait std::cmp::Eq
https://doc.rust-lang.org/std/cmp/trait.Eq.html - Struct std::boxed::Box
https://doc.rust-lang.org/std/boxed/struct.Box.html - Explore the ownership system in Rust
https://nercury.github.io/rust/guide/2015/01/19/ownership.html - Rust's ownership and move semantic
http://www.slideshare.net/saneyuki/rusts-ownership-and-move-semantics - Trait std::marker::Copy
https://doc.rust-lang.org/stable/std/marker/trait.Copy.html - Trait std::clone::Clone
https://doc.rust-lang.org/stable/std/clone/trait.Clone.html - The Stack and the Heap
https://doc.rust-lang.org/book/the-stack-and-the-heap.html - Rust Compare: Pointers & References
http://www.rust-compare.com/site/pointers.html - Rust Compare: Parameters
http://www.rust-compare.com/site/params.html - Why does this compile? Automatic dereferencing?
https://users.rust-lang.org/t/why-does-this-compile-automatic-dereferencing/2183 - Understanding Pointers, Ownership, and Lifetimes in Rust
http://koerbitz.me/posts/Understanding-Pointers-Ownership-and-Lifetimes-in-Rust.html - Rust lang series episode #25 — pointers (#rust-series)
https://steemit.com/rust-series/@jimmco/rust-lang-series-episode-25-pointers-rust-series - Rust – home page
https://www.rust-lang.org/en-US/ - Rust – Frequently Asked Questions
https://www.rust-lang.org/en-US/faq.html - Destructuring and Pattern Matching
https://pzol.github.io/getting_rusty/posts/20140417_destructuring_in_rust/ - The Rust Programming Language
https://doc.rust-lang.org/book/ - Rust (programming language)
https://en.wikipedia.org/wiki/Rust_%28programming_language%29 - Go – home page
https://golang.org/ - Stack Overflow – Most Loved, Dreaded, and Wanted language
https://stackoverflow.com/research/developer-survey-2016#technology-most-loved-dreaded-and-wanted - 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/ - Rust vs Go: My experience
https://www.reddit.com/r/golang/comments/21m6jq/rust_vs_go_my_experience/ - Friends of Rust (Organizations running Rust in production)
https://www.rust-lang.org/en-US/friends.html - Rust programs versus C++ g++
https://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=rust&lang2=gpp - Další benchmarky (nejedná se o reálné příklady „ze života“)
https://github.com/kostya/benchmarks - Go na Redditu
https://www.reddit.com/r/golang/ - Rust vs. Go
http://vschart.com/compare/rust/vs/go-language - Abstraction without overhead: traits in Rust
https://blog.rust-lang.org/2015/05/11/traits.html - Method Syntax
https://doc.rust-lang.org/book/method-syntax.html - Traits in Rust
https://doc.rust-lang.org/book/traits.html - Functional Programming in Rust – Part 1 : Function Abstraction
http://blog.madhukaraphatak.com/functional-programming-in-rust-part-1/ - 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 - Chytré ukazatele (moderní verze jazyka C++) [MSDN]
https://msdn.microsoft.com/cs-cz/library/hh279674.aspx - UTF-8 Everywhere
http://utf8everywhere.org/ - Rust by Example
http://rustbyexample.com/ - Rust oficiálně ve Fedoře
https://mojefedora.cz/rust-oficialne-ve-fedore/ - Resource acquisition is initialization
https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization - TIOBE index (October 2016)
http://www.tiobe.com/tiobe-index/ - Porovnání Go, D a Rustu na OpenHubu:
https://www.openhub.net/languages/compare?language_name[]=-1&language_name[]=-1&language_name[]=dmd&language_name[]=golang&language_name[]=rust&language_name[]=-1&measure=commits - String Types in Rust
http://www.suspectsemantics.com/blog/2016/03/27/string-types-in-rust/ - Trait (computer programming)
https://en.wikipedia.org/wiki/Trait_%28computer_programming%29 - Type inference
https://en.wikipedia.org/wiki/Type_inference