Obsah
1. Přetěžování operátorů a správa paměti v programovacím jazyku Rust
3. Trait specifikující chování operátoru +=
4. Měnitelná proměnná na levé straně operátoru +=
7. Explicitní volání destruktoru
8. Alokace paměti pro objekty ukládané na haldu (heap)
9. Základní použití traitu „Box“
10. Alokace komplexního čísla na haldě
11. Konstruktor pro alokaci na haldě
13. Repositář s demonstračními příklady
1. Přetěžování operátorů a správa paměti v programovacím jazyku Rust
V předchozí části seriálu o programovacím jazyku Rust jsme se mj. seznámili i se způsobem přetížení některých operátorů pro uživatelské datové typy, tj. pro datové struktury implementující traity s „předpisem“ metod, které s hodnotami uživatelských typů pracují popř. s nimi manipulují (modifikují je). Ukázali jsme si, jak lze přetížit operátor + a taktéž operátor == pro uživatelský datový typ představující jednu z možných implementací komplexních čísel. Operátory + a == jsou ve skutečnosti pro přetěžování vlastně velmi jednoduché, protože se v jejich implementaci nemusí (a neměly by) měnit hodnoty vstupních operandů – jinými slovy to znamená, že tyto operátory lze přetížit a používat i pro neměnné (immutable) hodnoty, což bylo ostatně v našich demonstračních příkladech splněno. Jen pro připomenutí, jak lze takové přetížení operátorů implementovat:
impl Add for Complex { type Output = Complex; fn add(self, right: Complex) -> Self::Output { Complex{real: self.real + right.real, imag: self.imag + right.imag} } }
impl PartialEq for Complex { fn eq(&self, right: &Complex) -> bool { self.real == right.real && self.imag == right.imag } }
2. Přetížení operátoru +=
Poněkud složitější situace nastane ve chvíli, kdy je nutné přetížit i ty operátory, u nichž se jeden z operandů mění. Jedná se především o všechny složené operátory, které vznikají kombinací nějakého binárního operátoru s operátorem přiřazení. Tyto operátory, které můžeme najít i v mnoha dalších programovacích jazycích, se zapisují následujícím způsobem:
lvalue OPERATOR= value
Přičemž by tento zápis měl být ekvivalentní zápisu:
lvalue = lvalue OPERATOR value
Příkladem může být složený operátor +=, který je zapotřebí implementovat jako kombinaci operátoru přiřazení a součtu. Tento operátor je implementován v traitu nazvaném AddAssign a v našem případě komplexních čísel může vypadat takto:
impl AddAssign for Complex { fn add_assign(&mut self, right: Complex) { *self = Complex::new(self.real + right.real, self.imag + right.imag) } }
Povšimněte si, jak se tento trait odlišuje od již zmíněného traitu Add:
- Není zde uveden řádek s typem výsledku (ani nemusí být, protože víme, že se modifikuje první operand).
- První operand musí být deklarován jako &mut (mutable).
- Výsledek je přiřazen, nikoli vrácen.
3. Trait specifikující chování operátoru +=
Následující demonstrační příklad vznikl rozšířením již minule popsaného příkladu. Nyní je však možné s komplexními čísly provádět i operace complex1+=complex2:
use std::ops::Add; use std::ops::AddAssign; use std::cmp::PartialEq; struct Complex { real: f32, imag: f32, } impl Complex { fn new(real: f32, imag: f32) -> Complex { Complex{real:real, imag:imag} } fn print(&self) { println!("complex number: {:}+{:}i", self.real, self.imag); } } impl Add<Complex> for Complex { type Output = Complex; fn add(self, right: Complex) -> Self::Output { Complex::new(self.real + right.real, self.imag + right.imag) } } impl Add<f32> for Complex { type Output = Complex; fn add(self, right: f32) -> Self::Output { Complex::new(self.real + right, self.imag) } } impl AddAssign for Complex { fn add_assign(&mut self, right: Complex) { *self = Complex::new(self.real + right.real, self.imag + right.imag) } } impl PartialEq for Complex { fn eq(&self, right: &Complex) -> bool { self.real == right.real && self.imag == right.imag } } fn main() { let c1 = Complex::new(1.0, 1.0); let c2 = Complex::new(3.0, 4.0); c1.print(); c2.print(); let c3 = c1 + c2; let c4 = Complex::new(4.0, 5.0); c3.print(); c4.print(); println!("c3 == c4? {}", (if c3==c4 { "yes"} else {"no"})); let c6 = Complex::new(1.0, 1.0); let c7 = Complex::new(3.0, 4.0); c6 += c7; c6.print(); }
4. Měnitelná proměnná na levé straně operátoru +=
Při pokusu o překlad předchozího kódu však překladač zcela správně vypíše chybové hlášení, protože se snažíme o změnu proměnné c6, která je neměnitelná (immutable):
error: cannot borrow immutable local variable `c6` as mutable --> 90_addassign_trait_no_mut.rs:69:5 | 67 | let c6 = Complex::new(1.0, 1.0); | -- use `mut c6` here to make mutable 68 | let c7 = Complex::new(3.0, 4.0); 69 | c6 += c7; | ^^ cannot borrow mutably error: aborting due to previous error
Úprava je velmi snadná a spočívá v náhradě řádku:
let c6 = Complex::new(1.0, 1.0);
za řádek:
let mut c6 = Complex::new(1.0, 1.0);
Celý zdrojový kód demonstračního příkladu nyní vypadá následovně:
use std::ops::Add; use std::ops::AddAssign; use std::cmp::PartialEq; struct Complex { real: f32, imag: f32, } impl Complex { fn new(real: f32, imag: f32) -> Complex { Complex{real:real, imag:imag} } fn print(&self) { println!("complex number: {:}+{:}i", self.real, self.imag); } } impl Add<Complex> for Complex { type Output = Complex; fn add(self, right: Complex) -> Self::Output { Complex::new(self.real + right.real, self.imag + right.imag) } } impl Add<f32> for Complex { type Output = Complex; fn add(self, right: f32) -> Self::Output { Complex::new(self.real + right, self.imag) } } impl AddAssign for Complex { fn add_assign(&mut self, right: Complex) { *self = Complex::new(self.real + right.real, self.imag + right.imag) } } impl PartialEq for Complex { fn eq(&self, right: &Complex) -> bool { self.real == right.real && self.imag == right.imag } } fn main() { let c1 = Complex::new(1.0, 1.0); let c2 = Complex::new(3.0, 4.0); c1.print(); c2.print(); let c3 = c1 + c2; let c4 = Complex::new(4.0, 5.0); c3.print(); c4.print(); println!("c3 == c4? {}", (if c3==c4 { "yes"} else {"no"})); let mut c6 = Complex::new(1.0, 1.0); let c7 = Complex::new(3.0, 4.0); c6 += c7; c6.print(); }
Tento program již pracuje podle očekávání:
complex number: 1+1i complex number: 3+4i complex number: 4+5i complex number: 4+5i c3 == c4? yes complex number: 4+5i
5. Trait „Drop“
V dalších kapitolách se zaměříme na popis způsobů alokace a dealokace objektů. Při sledování stavu objektů může být užitečné implementovat pro zvolený uživatelský datový typ trait nazvaný Drop. Tento trait obsahuje pouze jedinou metodu pojmenovanou drop, přičemž tato metoda je zavolána ve chvíli, kdy se program dostane na místo, odkud již není daný objekt dosažitelný (může se jednat o návrat z funkce, výskok z programového bloku atd.):
pub trait Drop { fn drop(&mut self); }
Ve chvíli, kdy je metoda drop zavolána, ještě objekt existuje, což znamená, že self je možné použít pro přístup ke složkám objektu, provést ruční dealokaci, zavření souborů či socketů atd. Teprve po výskoku z této metody je provedena automatická dealokace, což znamená, že drop vlastně nahrazuje klasické destruktory.
6. Implementace traitu „Drop“
Do příkladu s implementací jedné varianty komplexních čísel přidáme implementaci traitu Drop. Tato implementace nebude provádět žádný skutečný úklid objektu, pouze vypíše, který objekt je právě odstraňován z paměti. Vzhledem k tomu, že je při volání metody drop celý objekt ještě kompletní, lze bez problémů přistupovat k jeho prvkům (datovým složkám):
impl Drop for Complex { fn drop(&mut self) { println!("Dropping complex number: {:}+{:}i", self.real, self.imag); } }
Celý zdrojový kód další verze našeho demonstračního příkladu nyní vypadá takto:
use std::ops::Add; use std::ops::AddAssign; use std::cmp::PartialEq; struct Complex { real: f32, imag: f32, } impl Complex { fn new(real: f32, imag: f32) -> Complex { Complex{real:real, imag:imag} } fn print(&self) { println!("complex number: {:}+{:}i", self.real, self.imag); } } impl Add<Complex> for Complex { type Output = Complex; fn add(self, right: Complex) -> Self::Output { Complex::new(self.real + right.real, self.imag + right.imag) } } impl Add<f32> for Complex { type Output = Complex; fn add(self, right: f32) -> Self::Output { Complex::new(self.real + right, self.imag) } } impl AddAssign for Complex { fn add_assign(&mut self, right: Complex) { *self = Complex::new(self.real + right.real, self.imag + right.imag) } } impl PartialEq for Complex { fn eq(&self, right: &Complex) -> bool { self.real == right.real && self.imag == right.imag } } impl Drop for Complex { fn drop(&mut self) { println!("Dropping complex number: {:}+{:}i", self.real, self.imag); } } fn main() { let c1 = Complex::new(1.0, 1.0); let c2 = Complex::new(3.0, 4.0); c1.print(); c2.print(); let c3 = c1 + c2; let c4 = Complex::new(4.0, 5.0); c3.print(); c4.print(); println!("c3 == c4? {}", (if c3==c4 { "yes"} else {"no"})); let mut c6 = Complex::new(1.0, 1.0); let c7 = Complex::new(3.0, 4.0); c6 += c7; c6.print(); }
Po spuštění tohoto příkladu by se na standardní výstup měly vypsat následující zprávy:
complex number: 1+1i complex number: 3+4i Dropping complex number: 3+4i Dropping complex number: 1+1i complex number: 4+5i complex number: 4+5i c3 == c4? yes Dropping complex number: 1+1i Dropping complex number: 3+4i complex number: 4+5i Dropping complex number: 4+5i Dropping complex number: 4+5i Dropping complex number: 4+5i
7. Explicitní volání destruktoru
Destruktory, resp. přesněji řečeno metodu drop, se volají pouze automaticky ve chvíli, kdy se mění oblast platnosti objektu. Metodu drop nelze zavolat explicitně, o čemž se ostatně můžeme velmi snadno přesvědčit pokusem o překlad následujícího příkladu:
use std::ops::Add; use std::ops::AddAssign; use std::cmp::PartialEq; struct Complex { real: f32, imag: f32, } impl Complex { fn new(real: f32, imag: f32) -> Complex { Complex{real:real, imag:imag} } fn print(&self) { println!("complex number: {:}+{:}i", self.real, self.imag); } } impl Drop for Complex { fn drop(&mut self) { println!("Dropping complex number: {:}+{:}i", self.real, self.imag); } } fn main() { let c1 = Complex::new(1.0, 1.0); let c2 = Complex::new(3.0, 4.0); let c3 = Complex::new(0.0, 0.0); c1.print(); c2.print(); c3.print(); c1.drop(); c2.drop(); c3.drop(); }
Při pokusu o překlad se vypíše toto chybové hlášení:
error[E0040]: explicit use of destructor method --> test.rs:35:8 | 35 | c1.drop(); | ^^^^ call to destructor method error[E0040]: explicit use of destructor method --> test.rs:36:8 | 36 | c2.drop(); | ^^^^ call to destructor method error[E0040]: explicit use of destructor method --> test.rs:37:8 | 37 | c3.drop(); | ^^^^ call to destructor method error: aborting due to 3 previous errors
8. Alokace paměti pro objekty ukládané na haldu (heap)
Programovací jazyk Rust neobsahuje žádnou formu automatického správce paměti (garbage collector), který je použit, samozřejmě s výjimkou céčka a standardního C++, v prakticky všech vyšších programovacích jazycích. To v praxi znamená, že programátor musí explicitně stanovit, kdy a jakým způsobem jsou objekty alokovány a dealokovány. Prozatím jsme s tímto konceptem vlastně neměli žádný větší problém, a to z toho důvodu, že objekty jsou implicitně alokovány na zásobníku (stack), resp. přesněji řečeno v právě aktivním zásobníkovém rámci (stack frame). To je v mnoha ohledech výhodné, protože se po návratu z funkcí zásobníkový rámec (a tím pádem i objekty v něm uložené) automaticky uvolní z paměti, navíc nenastává ani fragmentace paměti atd. Ovšem ve chvíli, kdy je nutné vytvořit objekt viditelný i mimo aktivní zásobníkový rámec (či rámce nad ním), je nutné objekty alokovat na haldě (heapu). To se provádí s využitím speciálního konstruktoru Box::new(x:T), který alokuje novou oblast na haldě a posléze do této oblasti uloží x. Návratovým typem Box::new(x:T) je Box<T>.
9. Základní použití traitu „Box“
Podívejme se nyní na pravděpodobně úplně nejjednodušší příklad ukazující, jakým způsobem je možné alokovat nějaký objekt na haldě. Tímto objektem je zde myšlena celočíselná hodnota 42. Pokud v programu pouze zapíšeme:
fn foo() { let x = 42; println!("{}", x); }
vytvoří se proměnná x a na ní navázaná hodnota na zásobníku, resp. přesněji řečeno na zásobníkovém rámci. Životnost této proměnné i hodnoty navázané na proměnnou je omezena dobou, po kterou se řízení programu nachází v této funkci popř. ve funkcích volaných. Naproti tomu následující funkce:
fn main() { let x = Box::new(42); println!("{}", x); }
je poněkud komplikovanější, protože volání Box::new(42) alokuje oblast paměti na haldě a posléze do této oblasti uloží číslo 42. Následně se vrátí struktura, která mj. obsahuje i adresu alokované oblasti paměti na haldě, to vše je zabalené do objektu typu Box.
Navíc tento objekt implementuje i trait Drop, který zajistí automatické uvolnění oblasti alokované na haldě, což si ukážeme v navazujících příkladech. Povšimněte si, že se jedná o dosti podstatný rozdíl oproti mnoha dalším jazykům, které po alokaci vrací ukazatele či reference a ztrácí tak informace o tom, jaká je životnost objektu.
10. Alokace komplexního čísla na haldě
Následující příklad ukazuje, jakým způsobem můžeme na haldě alokovat prostor pro komplexní číslo, tj. pro datovou strukturu obsahující dva prvky typu f32: reálnou a imaginární složku. Ve skutečnosti je komplexní číslo vytvořeno na zásobníkovém rámci a až poté zkopírováno na haldu, ovšem právě tento typ operace je optimalizován (Rust je postaven nad LLVM), takže se většinou kopie bitového obrazu objektu zcela eliminuje:
struct Complex { real: f32, imag: f32, } impl Complex { fn new(real: f32, imag: f32) -> Complex { 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 x = Box::new(42); println!("{}", x); let c = Box::new(Complex::new(1.0, 2.0)); }
Po překladu a spuštění tohoto příkladu získáme na standardním výstupu následující dva řádky, které ukazují, že se Drop skutečně zavolá:
42 Dropping complex number: 1+2i
11. Konstruktor pro alokaci na haldě
Malým rozšířením zdrojového kódu můžeme zařídit, aby třída popisující komplexní čísla obsahovala i speciální typ konstruktoru, který vytvoří komplexní číslo přímo na haldě. Tento konstruktor se může jmenovat například new_on_heap. Tím, že samotné vytvoření komplexního čísla je použito ve volání konstruktoru Box::new() pomůžeme překladači programovacího jazyka Rust při optimalizaci výsledného strojového kódu:
fn new_on_heap(real: f32, imag: f32) -> Box<Complex> { Box::new(Complex{real:real, imag:imag}) }
Úplný zdrojový kód tohoto příkladu vypadá následovně:
struct Complex { real: f32, imag: f32, } impl Complex { fn new(real: f32, imag: f32) -> Complex { Complex{real:real, imag:imag} } fn new_on_heap(real: f32, imag: f32) -> Box<Complex> { Box::new(Complex{real:real, imag:imag}) } fn print(&self) { println!("complex number: {}+{}i", self.real, self.imag); } } impl Drop for Complex { fn drop(&mut self) { println!("Dropping complex number: {:}+{:}i", self.real, self.imag); } } fn main() { let c1 = Box::new(Complex::new(1.0, 2.0)); let c2 = Complex::new_on_heap(3.0, 4.0); c1.print(); c2.print(); }
Po spuštění příkladu se na standardní výstup vypíšou následující zprávy:
complex number: 1+2i complex number: 3+4i Dropping complex number: 3+4i Dropping complex number: 1+2i
12. Životnost objektů
V dalším, dnes již posledním, demonstračním příkladu si ukážeme, že destruktory objektů vytvořené implementací traitu Drop jsou skutečně volány ve chvíli, kdy dochází k ukončení životnosti (či viditelnosti) objektů. Nejedná se tedy o žádnou obdobu metody finalize známé z Javy, která může být volána mnohdy až dlouho poté, co je objekt nedosažitelný, nebo dokonce nemusí být volána vůbec:
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 fn2() { println!("fn2 begin"); let c = Box::new(Complex::new(2.0, 2.0)); println!("fn2 end"); } fn fn1() { println!("fn1 begin"); let c = Box::new(Complex::new(1.0, 1.0)); fn2(); println!("fn1 end"); } fn main() { println!("main begin"); let c = Box::new(Complex::new(0.0, 0.0)); fn1(); println!("main end"); }
Překladač sice bude při překladu tohoto demonstračního příkladu vypisovat varování o tom, že vytvořené objekty nejsou nikde použity, to však vůbec nevadí, protože zdrojový kód je v pořádku:
--> 95_lifetime.rs:23:9 | 23 | let c = Box::new(Complex::new(2.0, 2.0)); | ^ warning: unused variable: `c`, #[warn(unused_variables)] on by default --> 95_lifetime.rs:29:9 | 29 | let c = Box::new(Complex::new(1.0, 1.0)); | ^ warning: unused variable: `c`, #[warn(unused_variables)] on by default --> 95_lifetime.rs:36:9 | 36 | let c = Box::new(Complex::new(0.0, 0.0)); | ^
Zajímavé jsou pro nás výsledky běhu tohoto příkladu, protože v něm z funkce main voláme funkci fn1 a z ní funkci fn2. To samozřejmě ovlivňuje životnost lokálních proměnných (všechny se jmenují stejně) a tím pádem i alokaci/dealokaci komplexních čísel na haldě:
main begin Constructing complex number: 0+0i fn1 begin Constructing complex number: 1+1i fn2 begin Constructing complex number: 2+2i fn2 end Dropping complex number: 2+2i fn1 end Dropping complex number: 1+1i main end Dropping complex number: 0+0i
Vidíme, že komplexní číslo 0+0i bylo vytvořeno jako první ve funkci main a dealokace proběhla až na samotném konci programu, tj. ihned po opuštění funkce main. Podobně je tomu s životností komplexních čísel 1+1i a 2+2i (to má nejkratší životnost).
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/presentations. Příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý (dnes již poněkud objemný) repositář:
Příklad | Odkaz |
---|---|
90_addassign_trait_no_mut.rs | https://github.com/tisnik/presentations/blob/master/rust/90_addassign_trait_no_mut.rs |
91_addassign_trait.rs | https://github.com/tisnik/presentations/blob/master/rust/91_addassign_trait.rs |
92_drop_trait.rs | https://github.com/tisnik/presentations/blob/master/rust/92_drop_trait.rs |
93_box_new.rs | https://github.com/tisnik/presentations/blob/master/rust/93_box_new.rs |
94_box_new_complex.rs | https://github.com/tisnik/presentations/blob/master/rust/94_box_new_complex.rs |
95_lifetime.rs | https://github.com/tisnik/presentations/blob/master/rust/95_lifetime.rs |
99_explicit_drop.rs | https://github.com/tisnik/presentations/blob/master/rust/99_explicit_drop.rs |
14. Odkazy na Internetu
- 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