Obsah
2. Nepřímé volání destruktoru s využitím funkce std::mem::drop
3. Alokace objektů na haldě s počítáním referencí
4. Demonstrační příklad – alokace struktury představující komplexní číslo
5. Vytvoření většího množství referencí na jediný objekt alokovaný na haldě
6. Mutace objektu alokovaného na haldě, na nějž existuje větší množství referencí
7. Sémantika „move“ a referencované objekty na haldě
8. Sémantika „move“ a použití Rc::clone()
9. Reference na objekt vlastněná jiným objektem
10. Poznámka na závěr: použití datového typu Self
11. Repositář s demonstračními příklady
1. Přímé volání destruktoru?
V předchozí části seriálu o programovacím jazyku Rust jsme si řekli, že metodu drop() deklarovanou pro nějakou datovou strukturu, například pro naši implementaci komplexních čísel, není možné volat explicitně – toto zakázané volání je detekováno, podobně jako další sémantické chyby, již při překladu. Jen pro připomentí – v následujícím kódu se pokoušíme o explicitní volání destruktoru:
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(); // zakázáno, porušení je detekováno překladačem c2.drop(); // zakázáno, porušení je detekováno překladačem c3.drop(); // zakázáno, porušení je detekováno překladačem }
Při pokusu o překlad kódu s takto napsanou funkcí main() se vypíše následující 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
Ve skutečnosti však není nutné čekat na to, až se destruktor zavolá automaticky, protože existuje způsob nepřímého zavolání destruktoru. Podrobnosti si uvedeme v navazující kapitole.
2. Nepřímé volání destruktoru s využitím funkce std::mem::drop
Destruktor je možné v případě potřeby volat nepřímo, a to konkrétně s využitím funkce nazvané std::mem::drop(). U funkce z tohoto modulu není zapotřebí explicitně zapisovat jmenný prostor, proto ji můžeme volat pouze jako drop(). I přes stejné pojmenování je mezi následujícími programovými řádky dosti podstatný rozdíl, protože na druhém řádku se pokoušíme zavolat destruktor přímo (jako by se jednalo o běžnou funkci), zatímco na řádku třetím voláme funkci std::mem::drop, která sice taktéž zavolá destruktor, ale postará se i o zajištění celé sémantiky dealokace objektu (viz též sedmou část tohoto seriálu):
let c1 = Complex::new(1.0, 1.0); c1.drop(); // explicitní zavolání destruktoru - nelze provést drop(c1); // dealokace objektu - toto lze provést
Následuje výpis dvou prakticky shodných zdrojových kódů, které se od sebe odlišují pouze tím, že v prvním příkladu necháme na překladači, kam přesně umístí volání destruktoru (bude to ihned poté, co zanikne viditelnost proměnné, na níž je komplexní číslo navázáno), zatímco ve druhém příkladu se destruktor zavolá dříve (explicitním nepřímým zavoláním):
Zdrojový kód, v němž je volání destruktoru navázáno na oblast viditelnosti proměnných
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"); }
Zdrojový kód, v němž se destruktory volají explicitně (i když nepřímo)
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)); drop(c); println!("fn2 end"); } fn fn1() { println!("fn1 begin"); let c = Box::new(Complex::new(1.0, 1.0)); drop(c); fn2(); println!("fn1 end"); } fn main() { println!("main begin"); let c = Box::new(Complex::new(0.0, 0.0)); drop(c); fn1(); println!("main end"); }
Rozdíl ve výpisech obou příkladů nám naznačuje, kde přesně došlo k dealokaci komplexních čísel:
Automatické volání destruktorů "Vynucené" zavolání destruktorů -------------------------------------------------------------------------- main begin main begin Constructing complex number: 0+0i Constructing complex number: 0+0i fn1 begin Dropping complex number: 0+0i Constructing complex number: 1+1i fn1 begin fn2 begin Constructing complex number: 1+1i Constructing complex number: 2+2i Dropping complex number: 1+1i fn2 end fn2 begin Dropping complex number: 2+2i Constructing complex number: 2+2i fn1 end Dropping complex number: 2+2i Dropping complex number: 1+1i fn2 end main end fn1 end Dropping complex number: 0+0i main end
Poznámka: volání drop(c1) v našem případě odpovídá sémantice „move“ (protože jsme neimplementovali Copy či Clone traity), což znamená, že si překladač pohlídá následující (samozřejmě nekorektní) zápis, a to s použitím prostředků, o nichž jsme se již zmiňovali v předchozích částech (vlastnictví objektů):
fn fn2() { println!("fn2 begin"); let c = Box::new(Complex::new(2.0, 2.0)); drop(c); println!("{}", c.real); println!("fn2 end"); }
Při pokusu o překlad dostaneme po právu vynadáno, že se snažíme přistupovat k objektu předaného jinému vlastníkovi:
error[E0382]: use of moved value: `c.real` --> test.rs:25:20 | 24 | drop(c); | - value moved here 25 | println!("{}", c.real); | ^^^^^^ value used here after move <std macros>:2:27: 2:58 note: in this expansion of format_args! <std macros>:3:1: 3:54 note: in this expansion of print! (defined in <std macros>) test.rs:25:5: 25:28 note: in this expansion of println! (defined in <std macros>) | = note: move occurs because `c` has type `Box<Complex>`, which does not implement the `Copy` trait error: aborting due to previous error
Poznámka2: do drop() lze předat i referenci, potom se ovšem destruktor nezavolá, neboť vlastník se předáním reference nezmění. Ostatně si to můžete velmi snadno vyzkoušet:
fn fn2() { println!("fn2 begin"); let c = Box::new(Complex::new(2.0, 2.0)); drop(&c); println!("{}", c.real); println!("fn2 end"); }
3. Alokace objektů na haldě s počítáním referencí
Prozatím známe pouze jeden způsob alokace objektů na haldě, a to konkrétně použitím objektu typu Box. Připomeňme si, že po zápisu:
let c = Box::new(Complex::new(2.0, 2.0));
se na haldě (heap) alokuje místo pro datovou strukturu představující komplexní číslo. Adresa tohoto místa je uložena do objektu typu Box, kde je zapouzdřena, protože nás její konkrétní hodnota nezajímá, a tento objekt je následně vrácen a navázán na proměnnou c. Ve chvíli, kdy skončí viditelnost proměnné c, dojde i k uvolnění objektu z haldy. V průběhu tohoto procesu se zavolá destruktor. Samozřejmě je možné hodnotu z proměnné c přesunout (sémantika „move“) do jiné proměnné, další funkce atd.; vše je hlídáno překladačem, který v každém okamžiku ví, kdy se má objekt uvolnit.
Ovšem u mnoha programů potřebujeme zajistit odlišné chování. Konkrétně stále vyžadujeme vytvoření objektu na haldě, ovšem takovým způsobem, aby jsme se na něj mohli odkázat z více míst v programu (tedy aby byl objekt sdílený). V takovém případě nám chování objektů typu Box přestane vyhovovat a musíme se poohlédnout po jiném způsobu alokace. V programovacím jazyce Rust lze použít objekt typu Rc, kde „RC“ je odvozeno od výrazu „reference count(er)“. Interně je skutečně v Rc uložen atribut nesoucí informaci o počtu referencí existujících na objekt uložený na haldě (dokonce existuje i metoda vracející hodnotu tohoto atributu, tato metoda je však označena jako deprecated). Nová reference se získá zavoláním funkce Rc.clone(). Toto volání navíc zvýší počitadlo referencí o jedničku. Počitadlo je snižováno ve chvíli, kdy zanikne viditelnost proměnné, do něhož se reference uložila (stejně jako u Box). Ve chvíli, kdy počitadlo klesne k nule, je referencovaný objekt z haldy odstraněn a přitom se samozřejmě zavolá jeho destruktor.
Poznámka: pokud nepoužijeme Rc.clone(), nebude mezi chováním Rc a Box znatelný rozdíl.
Poznámka2: pokud se nějaký objekt má sdílet mezi větším množstvím vláken, musí se namísto Rc použít Arc s atomickou modifikací hodnoty počitadla referencí. Příklad si ukážeme příště.
4. Demonstrační příklad – alokace struktury představující komplexní číslo
Podívejme se na to, co se stane ve chvíli, kdy nahradíme alokaci komplexního čísla na haldě s použitím Box za alokaci s použitím Rc. V následujícím příkladu je přidán první řádek (use) a volání Box::new() bylo nahrazeno za Rc::new():
use std::rc::Rc; 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 = Rc::new(Complex::new(2.0, 2.0)); println!("fn2 end"); } fn fn1() { println!("fn1 begin"); let c = Rc::new(Complex::new(1.0, 1.0)); fn2(); println!("fn1 end"); } fn main() { println!("main begin"); let c = Rc::new(Complex::new(0.0, 0.0)); fn1(); println!("main end"); }
Po překladu a spuštění získáme na standardním výstupu následující řádky, které naznačují, že se chování Rc v tomto případě nijak neliší od chování Box:
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
5. Vytvoření většího množství referencí na jediný objekt alokovaný na haldě
Nyní se zaměřme na tu důležitou část alokace na haldě – na vlastní počítání referencí. V následujícím zdrojovém kódu jsou na haldě postupně alokována tři komplexní čísla, jejichž oblast životnosti je omezena viditelností proměnných navázaných do objekty typu Rc. Opět zde tedy není většího rozdílu oproti použití Box:
use std::rc::Rc; 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} } 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() { println!("main begin"); let c = Rc::new(Complex::new(0.0, 0.0)); c.print(); { println!("inner block begin"); let c2 = Rc::new(Complex::new(0.0, 0.0)); c2.print(); { println!("inmost block begin"); let c3 = Rc::new(Complex::new(0.0, 0.0)); c3.print(); println!("inmost block end"); } println!("inner block end"); } println!("main end"); }
Podle zpráv vypisovaných na standardní výstup je patrné, že se skutečně alokovala tři komplexní čísla a dealokace probíhala v opačném pořadí (stačí změnit hodnoty reálné či imaginární složky):
main begin Constructing complex number: 0+0i complex number: 0+0i inner block begin Constructing complex number: 0+0i complex number: 0+0i inmost block begin Constructing complex number: 0+0i complex number: 0+0i inmost block end Dropping complex number: 0+0i inner block end Dropping complex number: 0+0i main end Dropping complex number: 0+0i
Příklad však můžeme snadno změnit tak, aby všechny tři lokální proměnné c, c2 a c3 obsahovaly Rc s referencí na jediné komplexní číslo. Zde již musíme použít Rc.clone(), nestačí pouhé přiřazení:
use std::rc::Rc; 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} } 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() { println!("main begin"); let c = Rc::new(Complex::new(0.0, 0.0)); c.print(); { println!("inner block begin"); let c2 = c.clone(); c2.print(); { println!("inmost block begin"); let c3 = c.clone(); c3.print(); println!("inmost block end"); } println!("inner block end"); } println!("main end"); }
Chování programu se zásadním způsobem změní, protože se skutečně vytvoří jediné komplexní číslo, jehož destruktor je zavolán až ve chvíli, kdy skončí oblast viditelnosti proměnné c (tím se totiž sníží hodnota počitadla referencí na nulu):
main begin Constructing complex number: 0+0i complex number: 0+0i // nyní je počitadlo referencí nastaveno na 1 inner block begin // zvýšení počitadla referencí na 2 complex number: 0+0i inmost block begin // zvýšení počitadla referencí na 3 complex number: 0+0i inmost block end inner block end main end // proměnná c není viditelná → počitadlo kleslo na 0 → proběhne dealokace Dropping complex number: 0+0i
6. Mutace objektu alokovaného na haldě, na nějž existuje větší množství referencí
Zajímavé bude zjistit, co se stane ve chvíli, kdy změníme objekt na haldě a přitom na tento objekt bude existovat větší množství referencí. Existují dva způsoby mutace objektu, přičemž první způsob používá metodu Rc::get_mut() (ovšem jen ve chvíli, kdy existuje jen jedna reference) a druhý způsob používá metodu Rc::make_mut(), která v případě potřeby objekt naklonuje („clone on write“):
*Rc::get_mut(&mut c).unwrap() = Complex::new(100., 100.);
Způsob změny objektu předtím, než je na něj vytvořeno větší množství referencí, lze implementovat takto:
use std::rc::Rc; 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} } 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() { println!("main begin"); let mut c = Rc::new(Complex::new(0.0, 0.0)); print!("original value: "); c.print(); *Rc::get_mut(&mut c).unwrap() = Complex::new(100., 100.); print!("new value: "); c.print(); { println!("inner block begin"); let c2 = c.clone(); c2.print(); { println!("inmost block begin"); let c3 = c.clone(); c3.print(); println!("inmost block end"); } println!("inner block end"); } println!("main end"); }
Změna se projeví dealokací původního komplexního čísla a alokací (+ konstrukcí) čísla nového:
main begin Constructing complex number: 0+0i original value: complex number: 0+0i Constructing complex number: 100+100i Dropping complex number: 0+0i new value: complex number: 100+100i inner block begin complex number: 100+100i inmost block begin complex number: 100+100i inmost block end inner block end main end Dropping complex number: 100+100i
7. Sémantika „move“ a referencované objekty na haldě
V předchozím textu jsme si řekli, že pouhé předání objektu Rc odpovídá sémantice „move“, Jinými slovy: předáním objektu či jeho přiřazením do jiné proměnné dojde i ke změně vlastníka. To si můžeme ukázat na příkladu, v němž se pokusíme dvakrát zavolat funkci f2() a přitom jí předat stejný objekt (obsahující referenci na komplexní číslo uložené na haldě + počitadlo referencí):
fn f2(c:Rc<Complex>) { c.print(); }
Úplný zdrojový kód tohoto příkladu vypadá následovně:
use std::rc::Rc; 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} } 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 f1() -> Rc<Complex> { Rc::new(Complex::new(0.0, 0.0)) } fn f2(c:Rc<Complex>) { c.print(); } fn main() { println!("main begin"); let c = f1(); c.print(); f2(c); f2(c); }
Při překladu je změna vlastníka detekována, podobně jako je tomu i u dalších datových typů (Rc tedy není nijak výjimečný):
error[E0382]: use of moved value: `c` --> 104_rc_move_semantic_no_clone.rs:40:8 | 39 | f2(c); | - value moved here 40 | f2(c); | ^ value used here after move | = note: move occurs because `c` has type `std::rc::Rc<Complex>`, which does not implement the `Copy` trait error: aborting due to previous error
8. Sémantika „move“ a použití Rc::clone()
Předchozí příklad lze velmi snadno upravit takovým způsobem, aby pracoval podle očekávání. Postačuje naklonovat Rc, což interně znamená, že se zvýší počitadlo referencí a vrátí se Rc s referencí na to samé komplexní číslo. Uvolnění tohoto čísla z paměti bude provedeno ve chvíli, kdy bude počitadlo referencí sníženo na nulu:
let c = f1(); c.print(); f2(c.clone()); f2(c.clone());
Úplný zdrojový kód tohoto příkladu vypadá následovně:
use std::rc::Rc; 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} } 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 f1() -> Rc<Complex> { Rc::new(Complex::new(0.0, 0.0)) } fn f2(c:Rc<Complex>) { c.print(); } fn main() { println!("main begin"); let c = f1(); c.print(); f2(c.clone()); f2(c.clone()); }
Překlad takto upraveného příkladu proběhne korektně, zajímavé však bude sledovat chování programu v době běhu:
main begin Constructing complex number: 0+0i complex number: 0+0i complex number: 0+0i complex number: 0+0i end of main Dropping complex number: 0+0i
Vidíme, že komplexní číslo bylo vytvořeno (zkonstruováno) jen jedenkrát, uvolněno z paměti bylo až za koncem funkce main.
9. Reference na objekt vlastněná jiným objektem
Reálnému světu bude bližší následující příklad, v němž je deklarována datová struktura nazvaná ComplexNumberOwner. Tato struktura má dva prvky – celočíselný identifikátor a Rc s referencí na komplexní číslo alokované na haldě:
struct ComplexNumberOwner { id: i32, value: Rc<Complex> }
Pokud vytvoříme větší množství těchto struktur (pole, vektor), lze mezi nimi v případě potřeby komplexní čísla sdílet. V následujícím kódu budou první tři struktury ComplexNumberOwner „vlastnit“ stejné komplexní číslo:
let c1 = Rc::new(Complex::new(1.0, 1.0)); let c2 = Rc::new(Complex::new(2.0, 2.0)); let owner1 = ComplexNumberOwner{id:1, value: c1.clone()}; let owner2 = ComplexNumberOwner{id:2, value: c1.clone()}; let owner3 = ComplexNumberOwner{id:3, value: c1.clone()}; let owner4 = ComplexNumberOwner{id:4, value: c2.clone()};
Úplný zdrojový kód tohoto příkladu vypadá následovně:
use std::rc::Rc; 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} } 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); } } struct ComplexNumberOwner { id: i32, value: Rc<Complex> } impl ComplexNumberOwner { fn print(&self) { println!("owner: number #{} with value {}+{}i", self.id, self.value.real, self.value.imag); } } fn main() { let c1 = Rc::new(Complex::new(1.0, 1.0)); let c2 = Rc::new(Complex::new(2.0, 2.0)); let owner1 = ComplexNumberOwner{id:1, value: c1.clone()}; let owner2 = ComplexNumberOwner{id:2, value: c1.clone()}; let owner3 = ComplexNumberOwner{id:3, value: c1.clone()}; let owner4 = ComplexNumberOwner{id:4, value: c2.clone()}; owner1.print(); owner2.print(); owner3.print(); owner4.print(); }
Chování příkladu po spuštění:
Constructing complex number: 1+1i Constructing complex number: 2+2i owner: number #1 with value 1+1i owner: number #2 with value 1+1i owner: number #3 with value 1+1i owner: number #4 with value 2+2i Dropping complex number: 2+2i Dropping complex number: 1+1i
Vidíme, že první tři objekty typu ComplexNumberOwner skutečně vlastní stejné komplexní číslo, které je následně po opuštění funkce main korektně uvolněno z paměti.
10. Poznámka na závěr: použití datového typu Self
Na závěr si uvedeme poměrně užitečný trik, který souvisí s datovým typem Self (musí se psát s velkým písmenem na začátku). Použití Self si nejlépe ukážeme na příkladu. Prozatím jsme ve všech demonstračních příkladech s komplexními čísly používali tento konstruktor, resp. přesněji řečeno metodu, která se jako konstruktor chovala:
fn new(real: f32, imag: f32) -> Complex { Complex{real:real, imag:imag} }
Ve skutečnosti však překladač ví, v jakém kontextu je metoda deklarována, protože je umístěna do bloku impl Complex. V tomto kontextu je možné použít datový typ Self, což je vlastně pouze syntaktický cukr umožňující, aby se nemusel pořád opisovat datový typ (většinou není tvořen jen identifikátorem). Konstruktor lze tedy napsat i následovně:
fn new(real: f32, imag: f32) -> Self { Complex{real:real, imag:imag} }
Pozor však na to, že následující zápis již není možný, protože Self zastupuje skutečný datový typ jen při deklaraci parametrů funkcí/metod a jejích návratových typů:
fn new(real: f32, imag: f32) -> Self { Self{real:real, imag:imag} }
Navíc platí, že pokud je první parametr metody pojmenovaný self, je překladačem chápán jako self:Self. Totéž platí i pro další modifikace, tedy: &self znamená self: &Self a &mut self znamená self: &mut Self. To jsme ostatně již bez dalšího vysvětlení použili v deklaraci destruktoru:
impl Drop for Complex { fn drop(&mut self) { println!("Dropping complex number: {:}+{:}i", self.real, self.imag); } } impl Drop for Complex { fn drop(self: &mut Self) { println!("Dropping complex number: {:}+{:}i", self.real, self.imag); } }
Podívejme se na úpravu příkladu s komplexními čísly, kde je tento syntaktický cukr použit:
struct Complex { real: f32, imag: f32, } impl Complex { fn new(real: f32, imag: f32) -> Self { 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 c = Box::new(Complex::new(1.0, 2.0)); }
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/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 |
---|---|
96_explicit_drop.rs | https://github.com/tisnik/presentations/blob/master/rust/96_explicit_drop.rs |
97_rc.rs | https://github.com/tisnik/presentations/blob/master/rust/97_rc.rs |
101_rc_no_clone.rs | https://github.com/tisnik/presentations/blob/master/rust/101_rc_no_clone.rs |
102_rc_clone.rs | https://github.com/tisnik/presentations/blob/master/rust/102_rc_clone.rs |
103_rc_get_mut.rs | https://github.com/tisnik/presentations/blob/master/rust/103_rc_get_mut.rs |
104_rc_move_semantic_no_clone.rs | https://github.com/tisnik/presentations/blob/master/rust/104_rc_move_semantic_no_clone.rs |
105_rc_move_semantic_clone.rs | https://github.com/tisnik/presentations/blob/master/rust/105_rc_move_semantic_clone.rs |
106_owned_references.rs | https://github.com/tisnik/presentations/blob/master/rust/106_owned_references.rs |
12. Odkazy na Internetu
- 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 - Module std::rc
https://doc.rust-lang.org/std/rc/ - Struct std::sync::Arc
https://doc.rust-lang.org/std/sync/struct.Arc.html - Module std::sync::atomic
https://doc.rust-lang.org/std/sync/atomic/ - 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