Obsah
1. Struktury, přístup k prvkům struktur
2. Pořadí inicializace prvků struktury
3. Měnitelné a neměnitelné struktury
4. Částečná kopie prvků struktury do struktury jiné
6. Destructuring n-tice a struktury
7. Deklarace typu prvků n-tice
8. Speciální případ – n-tice s jediným prvkem
9. Předání struktury do funkce
10. Předání struktury do funkce přes referenci
11. Změna obsahu struktury ve funkci
12. Vlastnictví objektů a sémantika „move“
14. Mutace naklonované struktury
15. Pravidlo pro použití rysů copy a clone
1. Struktury, přístup k prvkům struktur
V programovacím jazyku Rust je možné deklarovat takzvané struktury (struct), které jsou někdy známé i pod jménem záznamy (records). Struktury obsahují datové položky, které jsou jednoznačně pojmenované (v rámci jmenného prostoru struktury) a je pro ně definován i datový typ. Zachováno je i pořadí položek ve struktuře, což může být v některých případech důležité. Deklarací struktury vzniká nový uživatelský datový typ. Zkusme si například vytvořit strukturu představující komplexní číslo, v němž je reálná a imaginární složka tvořena hodnotami typu f32 (32bitové číslo s plovoucí řádovou čárkou):
struct Complex { real: f32, imag: f32, }
Poté můžeme velmi snadno vytvořit hodnotu typu Complex a přiřadit ji do proměnné c1. Následně je možné přistupovat k položkám struktury pomocí zápisu proměnná.položka, což se nijak neliší od dalších programovacích jazyků:
fn main() { let c1 = Complex{real:0.0, imag:0.0}; println!("complex number: {}+{}i", c1.real, c1.imag); }
Samozřejmě je možné explicitně specifikovat typ proměnné (vyznačená část kódu), jinak si typ překladač odvodí automaticky:
struct Complex { real: f32, imag: f32, } fn main() { let c1:Complex = Complex{real:0.0, imag:0.0}; println!("complex number: {}+{}i", c1.real, c1.imag); }
2. Pořadí inicializace prvků struktury
Vzhledem k tomu, že se při inicializaci prvků struktury musí jednotlivé prvky explicitně pojmenovat, nezáleží na jejich pořadí. V následujícím příkladu se nejprve inicializuje imaginární položka a poté položka reálná, výsledkem ale bude stejný objekt, jako v předchozím příkladu:
struct Complex { real: f32, imag: f32, } fn main() { // nezalezi na poradi inicializace prvku let c1 = Complex{imag:0.0, real:0.0}; println!("complex number: {}+{}i", c1.real, c1.imag); }
3. Měnitelné a neměnitelné struktury
Podobně, jako je tomu i u dalších typů proměnných, jsou i struktury implicitně neměnitelné. Pokud se pokusíme o změnu prvků struktury (tedy o zápis), je tato operace odhalena už při překladu:
struct Complex { real: f32, imag: f32, } fn main() { let c1 = Complex{real:0.0, imag:0.0}; c1.real = 10.0; c1.imag = 20.0; println!("complex number: {}+{}i", c1.real, c1.imag); }
Překladač by měl vypsat přibližně toto chybové hlášení, z něhož je jasně patrné, že se pokoušíme přepsat neměnitelnou (immutable) hodnotu:
error: cannot assign to immutable field `c1.real` --> 51_mutable_struct1_err.rs:8:5 | 8 | c1.real = 10.0; | ^^^^^^^^^^^^^^ error: cannot assign to immutable field `c1.imag` --> 51_mutable_struct1_err.rs:9:5 | 9 | c1.imag = 20.0; | ^^^^^^^^^^^^^^ error: aborting due to 2 previous errors
Pokud se programátoři setkají s předchozím chybovým hlášením, mohou se pokusit o úpravu deklarace struktury, konkrétně o přidání modifikátoru mut k datovým položkám (k této úpravě mohou „lákat“ i chybová hlášení zobrazená před tímto odstavcem). Zkusme si tedy příklad upravit:
struct Complex { mut real: f32, mut imag: f32, } fn main() { let c1 = Complex{real:0.0, imag:0.0}; c1.real = 10.0; c1.imag = 20.0; println!("complex number: {}+{}i", c1.real, c1.imag); }
Tato úprava ovšem odporuje syntaxi programovacího jazyka Rust a je špatná i sémanticky, protože rys měnitelnosti či neměnitelnosti (mutability/immutability) se váže nikoli k datovému typu, ale k proměnné. Ostatně ani překladač nebude příliš spokojený:
error: expected identifier, found keyword `mut` --> 52_mutable_struct2_err.rs:2:5 | 2 | mut real: f32, | ^^^ error: expected `:`, found `real` --> 52_mutable_struct2_err.rs:2:9 | 2 | mut real: f32, | ^^^^ error: aborting due to previous error
Korektní vytvoření měnitelné proměnné typu struktura vypadá následovně:
struct Complex { real: f32, imag: f32, } fn main() { let mut c1 = Complex{real:0.0, imag:0.0}; println!("complex number: {}+{}i", c1.real, c1.imag); c1.real = 10.0; c1.imag = 20.0; println!("complex number: {}+{}i", c1.real, c1.imag); }
Samozřejmě je možné bez problémů vytvořit jak měnitelnou, tak i neměnitelnou strukturu (což je jeden z důvodů, proč je vlastnost měnitelnosti/neměnitelnosti vztažená k proměnným):
struct Complex { real: f32, imag: f32, } fn main() { let mut c1 = Complex{real:0.0, imag:0.0}; let c2 = Complex{real:2.0, imag:4.0}; println!("complex number #1: {}+{}i", c1.real, c1.imag); println!("complex number #2: {}+{}i", c2.real, c2.imag); c1.real = 10.0; c1.imag = 20.0; println!("complex number: {}+{}i", c1.real, c1.imag); }
4. Částečná kopie prvků struktury do struktury jiné
Programovací jazyk Rust podporuje i vytvoření nové struktury s tím, že se hodnoty jednotlivých položek zkopírují ze struktury jiné. Syntaxe zápisu je patrná z následujícího příkladu. Proměnná c2 je navázána na komplexní číslo, jehož reálná složka je určena explicitně a složka imaginární je zkopírována. Proměnná c3 vznikla kompletní kopií c2:
struct Complex { real: f32, imag: f32, } fn main() { let c1 = Complex{real:0.0, imag:0.0}; let c2 = Complex{real:2.0, ..c1}; let c3 = Complex{..c2}; println!("complex number 1: {}+{}i", c1.real, c1.imag); println!("complex number 2: {}+{}i", c2.real, c2.imag); println!("complex number 3: {}+{}i", c3.real, c3.imag); }
O tom, že se provede kopie položek, se snadno přesvědčíme:
struct Complex { real: f32, imag: f32, } fn main() { let mut c1 = Complex{real:0.0, imag:0.0}; let mut c2 = Complex{real:2.0, ..c1}; let mut c3 = Complex{..c2}; println!("complex number 1: {}+{}i", c1.real, c1.imag); println!("complex number 2: {}+{}i", c2.real, c2.imag); println!("complex number 3: {}+{}i", c3.real, c3.imag); c1.imag = 100.0; println!("complex number 1: {}+{}i", c1.real, c1.imag); println!("complex number 2: {}+{}i", c2.real, c2.imag); println!("complex number 3: {}+{}i", c3.real, c3.imag); }
Po spuštění:
complex number 1: 0+0i complex number 2: 2+0i complex number 3: 2+0i complex number 1: 0+100i complex number 2: 2+0i complex number 3: 2+0i
Změna imaginární složky komplexního čísla c1 nemá vliv na čísla c2 a c3.
5. N-tice (tuple)
Jedním ze základních strukturovaných datových typů podporovaných jazykem Rust jsou n-tice (tuple). Prvky n-tice jsou uspořádány (v pořadí jejich zápisu) a nemusí být stejného datového typu. Konstruktor pro vytvoření n-tice je podobný zápisu známému z Pythonu – prvky n-tice se zapisují do kulatých závorek a oddělují se čárkami (s jednou výjimkou specifikovanou níže). Zajímavý je způsob přístupu k prvkům n-tice, protože se používá zápis n_tice.index (s tečkou, nikoli s hranatými závorkami), přičemž indexy začínají nulou, stejně jako u polí. Podívejme se na jednoduchý příklad:
fn main() { let c1 = (1.0, 2.0); let c2 = (0.0, 0.0); println!("complex number 1: {}+{}i", c1.0, c1.1); println!("complex number 2: {}+{}i", c2.0, c2.1); }
N-tice lze samozřejmě předávat jako parametry funkcí a funkce mohou n-tice vracet jako svoji návratovou hodnotu. Jak se dozvíme v dalším textu, podporují n-tice sémantiku Copy a rys Clone (to ovšem jen za předpokladu, že ji podporují i všechny prvky n-tice, splněno je to pro primitivní datové typy atd.).
Samozřejmě jsou podporovány i n-tice obsahující další n-tice. Při přístupu k prvkům takové datové struktury je nutné si dát pozor na správné uzávorkování (nestačí jen napsat x.1.3.1):
fn main() { let x = (1.0, (2, 3, 4, (true, false))); println!("{}", x.0); println!("{}", (x.1).1); println!("{}", ((x.1).3).1); }
6. Destructuring n-tice a struktury
V některých programovacích jazycích se setkáme s termínem destructuring, kterým se většinou označuje „rozložení“ nějaké datové struktury na položek a přiřazení těchto položek do proměnných (viz například Python či Clojure). V programovacím jazyce Rust podobnou funkcionalitu taktéž nalezneme; je součástí obecnějšího konceptu nazývaného „pattern matching“, o němž jsme se již částečně zmínili v úvodních článcích. Podívejme se, jak je možné nejprve vytvořit dvouprvkovou n-tici, uložit ji do proměnné c1 a následně tuto n-tici rozložit na jednotlivé položky, které se uloží do proměnných real a imag (Pythonisti by neměli zapomenout na závorky):
fn main() { let c1 = (1.0, 2.0); let (real, imag) = c1; println!("complex number: {}+{}i", real, imag); }
Destructuring pro struktury se zapisuje následovně (opět se jedná o součást konceptu pattern matchingu). Povšimněte si, že nové proměnné se jmenují stejně, jako položky struktury:
struct Complex { real: f32, imag: f32, } fn main() { let c1 = Complex{imag:0.0, real:0.0}; let Complex{real,imag} = c1; println!("complex number: {}+{}i", real, imag); }
Popř. je možné explicitně zapsat nová jména pro proměnné:
struct Complex { real: f32, imag: f32, } fn main() { let c1 = Complex{imag:0.0, real:0.0}; let Complex{real:r,imag:i} = c1; println!("complex number: {}+{}i", r, i); }
7. Deklarace typu prvků n-tice
Pokud je to nutné, lze explicitně deklarovat typy prvků n-tice. Většinou to sice není zapotřebí, protože si typy dokáže odvodit samotný překladač, ale někdy může být vhodné se vyjádřit explicitně. V tomto případě vypadá zápis následovně:
fn main() { let c1:(f32,f32) = (1.0, 2.0); let c2:(f32,f32) = (0.0, 0.0); println!("complex number 1: {}+{}i", c1.0, c1.1); println!("complex number 2: {}+{}i", c2.0, c2.1); }
Připomeňme si, že podobně (tedy za dvojtečkou zapsanou za jménem proměnné) jsme zapisovali i specifikaci jiných datových typů, takže se v případě n-tic vlastně nejedná o nic nového:
let mut i:i8 = 0;
8. Speciální případ – n-tice s jediným prvkem
Speciálním případem, který dobře znají například programátoři používající programovací jazyk Python, je n-tice obsahující jediný prvek. Taková n-tice není nijak zajímavá ze sémantického hlediska, ovšem z hlediska syntaktického ano, protože prvky n-tice se zapisují do kulatých závorek, které mají v programovacím jazyku Rust i další významy. Aby se skutečně zajistilo, že překladač pochopí zápis jednoprvkové n-tice, je nutné za tímto prvkem zapsat čárku:
fn main() { let x = (1.0); let y = (1.0,); println!("x {}", x); println!("y {}", y.0); }
V tomto příkladu je do proměnné x přiřazena hodnota 10 (vypočtená výrazem v závorkách) a do proměnné y je přiřazena jednoprvková n-tice.
9. Předání struktury do funkce
Struktury je možné (zdánlivě bez problémů) předat nějaké funkci, což by nemělo být příliš překvapivé, protože po deklaraci struktury se jedná o plnohodnotný datový typ. V následujícím příkladu je ukázáno, jakým způsobem se předání může provést:
struct Complex { real: f32, imag: f32, } fn print_complex(c:Complex) { println!("complex number: {}+{}i", c.real, c.imag); } fn main() { let c1 = Complex{real:1.0, imag:2.0}; println!("complex number: {}+{}i", c1.real, c1.imag); print_complex(c1); }
Tento příklad bude fungovat bez problémů, takže by se mohlo zdát, že předávání parametrů v Rustu pracuje stejně jako například v céčku. Ve skutečnosti je však sémantika zásadně odlišná, protože pokud je nějaký objekt předán funkci (nebo jen přiřazen k jiné proměnné), stává se tato funkce vlastníkem objektu a současně původní vlastník objektu (ve funkci main) toto vlastnictví ztrácí. Co to znamená v praxi ukazuje další příklad, v němž zavoláme funkci print_complex(), která se stane vlastníkem objektu navázaného na proměnnou c1. Z tohoto důvodu poslední příkaz ve funkci main nepůjde přeložit:
struct Complex { real: f32, imag: f32, } fn print_complex(c:Complex) { println!("complex number: {}+{}i", c.real, c.imag); } fn main() { let c1 = Complex{real:1.0, imag:2.0}; println!("complex number: {}+{}i", c1.real, c1.imag); print_complex(c1); println!("complex number: {}+{}i", c1.real, c1.imag); }
Z chybového hlášení jsem pro přehlednost odstranil nerelevantní řádky týkající se expanze makra println!. Význam některých termínů („move“, „Copy trait“) bude vysvětlen v navazujících kapitolách, konkrétně v kapitole 12 a 13:
error[E0382]: use of moved value: `c1.real` --> 60_passing_struct_err.rs:14:40 | 13 | print_complex(c1); | -- value moved here 14 | println!("complex number: {}+{}i", c1.real, c1.imag); | ^^^^^^^ value used here after move 60_passing_struct_err.rs:14:5: 14:58 note: in this expansion of println! (defined in ) | = note: move occurs because `c1` has type `Complex`, which does not implement the `Copy` trait error[E0382]: use of moved value: `c1.imag` --> 60_passing_struct_err.rs:14:49 | 13 | print_complex(c1); | -- value moved here 14 | println!("complex number: {}+{}i", c1.real, c1.imag); | ^^^^^^^ value used here after move 60_passing_struct_err.rs:14:5: 14:58 note: in this expansion of println! (defined in ) | = note: move occurs because `c1` has type `Complex`, which does not implement the `Copy` trait error: aborting due to 2 previous errors
10. Předání struktury do funkce přes referenci
Předchozí problém je možné obejít předáním komplexního čísla referencí, což je téma, kterému jsme se věnovali minule. Povšimněte si, že se ve funkci print_complex stále přistupuje k prvkům struktury s využitím operátoru tečky a nikoli přes operátor ->, který čtenáři pravděpodobně znají z céčka a C++:
struct Complex { real: f32, imag: f32, } fn print_complex(c:&Complex) { println!("complex number: {}+{}i", c.real, c.imag); } fn main() { let c1 = Complex{real:1.0, imag:2.0}; println!("complex number: {}+{}i", c1.real, c1.imag); print_complex(&c1); println!("complex number: {}+{}i", c1.real, c1.imag); }
11. Změna obsahu struktury ve funkci
Pokud budete potřebovat změnit obsah struktury ve funkci, musí se samozřejmě použít modifikátor mut, a to jak při uložení struktury do proměnné, tak i v deklaraci funkce. Překlad následujícího příkladu skončí s chybou, protože se pokoušíme změnit položku neměnitelné hodnoty:
struct Complex { real: f32, imag: f32, } fn print_complex(c:&Complex) { println!("complex number: {}+{}i", c.real, c.imag); } fn add_real(c:&Complex,real:f32) { c.real += real; } fn main() { let c1 = Complex{real:1.0, imag:2.0}; print_complex(&c1); add_real(&c1, 41.); print_complex(&c1); }
error: cannot assign to immutable field `c.real` --> test.rs:11:5 | 11 | c.real += real; | ^^^^^^^^^^^^^^ error: aborting due to previous error
Stačí však malá úprava spočívající v použití modifikátoru mut:
struct Complex { real: f32, imag: f32, } fn print_complex(c:&Complex) { println!("complex number: {}+{}i", c.real, c.imag); } fn add_real(c:&mut Complex,real:f32) { c.real += real; } fn main() { let mut c1 = Complex{real:1.0, imag:2.0}; print_complex(&c1); add_real(&mut c1, 41.); print_complex(&c1); }
Povšimněte si, že překladači nelze „vnutit“, aby neměnitelnou strukturu začal považovat za strukturu měnitelnou:
struct Complex { real: f32, imag: f32, } fn print_complex(c:&Complex) { println!("complex number: {}+{}i", c.real, c.imag); } fn add_real(c:&mut Complex,real:f32) { c.real += real; } fn main() { let c1 = Complex{real:1.0, imag:2.0}; print_complex(&c1); add_real(&mut c1, 41.); print_complex(&c1); }
Překlad tohoto příkladu skončí s následující chybou:
error: cannot borrow immutable local variable `c1` as mutable --> test.rs:17:19 | 15 | let c1 = Complex{real:1.0, imag:2.0}; | -- use `mut c1` here to make mutable 16 | print_complex(&c1); 17 | add_real(&mut c1, 41.); | ^^ cannot borrow mutably error: aborting due to previous error
12. Vlastnictví objektů a sémantika „move“
Vraťme se ještě k problematice předávání a vypůjčování vlastnictví objektů, s nímž jsme se již setkali v deváté kapitole. Zkusme si vytvořit jednodušší příklad, v němž vytvoříme komplexní číslo, přiřadíme ho k proměnné c1, posléze ho přiřadíme k proměnné c2 a následně proměnnou c2 použijeme při volání funkce print_complex:
struct Complex { real: f32, imag: f32, } fn print_complex(c:Complex) { println!("complex number: {}+{}i", c.real, c.imag); } fn main() { let c1 = Complex{real:1.0, imag:2.0}; let c2 = c1; print_complex(c2); }
Tento příklad bude fungovat, protože se vlastnictví komplexního čísla postupně předává od c1 přes c2 až do funkce print_complex. Ovšem již při nepatrné změně dojde k chybě při překladu, protože se pokoušíme přistoupit ke komplexnímu číslu přes c1, které ho již nevlastní:
struct Complex { real: f32, imag: f32, } fn print_complex(c:Complex) { println!("complex number: {}+{}i", c.real, c.imag); } fn main() { let c1 = Complex{real:1.0, imag:2.0}; let c2 = c1; print_complex(c1); }
V tomto případě překladač zobrazí následující chybové hlášení u posledního programového řádku:
error[E0382]: use of moved value: `c1` --> test.rs:13:19 | 12 | let c2 = c1; | -- value moved here 13 | print_complex(c1); | ^^ value used here after move | = note: move occurs because `c1` has type `Complex`, which does not implement the `Copy` trait error: aborting due to previous error
Důležité je si uvědomit, proč vlastně překladač hlídá vlastnictví objektů. Předpokládejme, že namísto pouhých struktur pracujeme například s vektory. Ty jsou představovány objekty se dvěma částmi – jedna část obsahuje prvky vektoru, druhá část pak různé další atributy, například délku vektoru. Přiřazení typu let v1=vec![1,2,3]; let v2=v1; musí mj. zajistit, aby se při volání destruktorů (zde konkrétně na konci bloku) vektor z paměti uvolnil jen jedenkrát, popř. aby přiřazení provedla „hlubokou kopii“ (deep copy) objektu (tím pádem se destruktor zavolá pro každý objekt zvlášť, protože po provedení hluboké kopie/naklonování získáme dva samostatné objekty). V jazyce Rust je implicitně podporována takzvaná sémantika „move“ znamenající, že po přenesení vlastnictví již není možné původní proměnnou (či parametr) použít a tudíž se ani nemusíme starat o to, kdo a v jakém okamžiku bude volat destruktory.
13. Sémantika „copy“
Kromě sémantiky „move“ je možné (explicitně) použít takzvanou sémantiku „copy“, která určuje, že se namísto předání vlastnictví, například volané funkci, provede zkopírování hodnoty (ve skutečnosti se zde ponechávají možnosti pro optimalizace na úrovni LLVM, ale tím se prozatím nebudeme zabývat). Z pohledu programátora je zajištění takové funkcionality pro struktury jednoduché – musí pouze specifikovat, že jeho struktura má rys (trait) Copy a Clone:
#[derive(Copy, Clone)] struct Complex { real: f32, imag: f32, } fn print_complex(c:Complex) { println!("complex number: {}+{}i", c.real, c.imag); } fn main() { let c1 = Complex{real:1.0, imag:2.0}; print_complex(c1); let c2 = c1; print_complex(c2); }
Poznámka: nejsem si jistý tím, jestli se trait má skutečně překládat jako „rys“ či zda vůbec oficiální překlad existuje, ovšem další možnosti „atribut“ či „vlastnost“ už se používají v odlišném významu (pro překlad slov attribute a property). Prozatím můžeme trait pokládat za rozšířené rozhraní, které kromě hlaviček metod obsahuje i jejich těla, ale už nikoli stavové informace (podobně jako rozhraní v Javě 8, které ovšem nemá všechny vlastnosti traitů). To, že má nějaký typ či objekt určitý rys (trait) tedy není zajištěno přímým děděním.
Specifikací rysu Clone je specifikováno, že se má namísto předání vlastnictví vytvořit hluboká kopie objektu (zde tedy našeho komplexního čísla). Interně se hluboká kopie implementuje ve funkci .clone(). Specifikace rysu Copy určuje již zmíněnou sémantiku „copy“. Je nutné dávat pozor na to, že rys Copy vyžaduje i implementaci rysu Clone (naopak to však neplatí). To znamená, že můžeme mít strukturu, která je klonovatelná, ale nepoužije se pro ni sémantika „copy“.
Poznámka: základní datové typy implementují rys Clone, samotné přiřazení je zde navíc jednodušší, protože postačuje mělká kopie.
14. Mutace naklonované struktury
Pokud je objekt, resp. v našem případě datová struktura naklonován, je umístěn do odlišné oblasti operační paměti, než původní objekt. To je sice pro neměnitelné (immutable) datové struktury jen nepatrná změna, ovšem v následujícím demonstračním příkladu je ukázáno, co se stane, když použijeme měnitelné datové struktury. První struktura je zkonstruována a přiřazena k proměnné c1 s modifikátorem mut, což umožní změnu datových položek. Posléze je provedena hluboká kopie a přiřazení této kopie k proměnné c2, taktéž s modifikátorem mut:
#[derive(Copy, Clone)] struct Complex { real: f32, imag: f32, } fn print_complex(c:Complex) { println!("complex number: {}+{}i", c.real, c.imag); } fn main() { let mut c1 = Complex{real:1.0, imag:2.0}; print_complex(c1); let mut c2 = c1; c1.real = 0.; c2.real = 1000.; print_complex(c1); print_complex(c2); }
Po spuštění příkladu je patrné, že změna datové položky jedné struktury nemá žádný vliv na stejně pojmenovanou datovou položku struktury druhé, tj. struktury začaly být na sobě nezávislé (to je přesně ta sémantika, kterou jsme vyžadovali):
complex number: 1+2i complex number: 0+2i complex number: 1000+2i
15. Pravidlo pro použití rysů copy a clone
Zkusme si nyní změnit naši datovou strukturu Complex takovým způsobem, že její prvky nebudou typu f32 (tedy 32bitové číslo s plovoucí řádovou čárkou), ale bude se jednat o další uživatelsky definovanou strukturu nazvanou Coordinate. Tato struktura obsahuje jen jedinou položku, která je typu f32. Upravený program vypadá následovně:
struct Coordinate { value: f32 } #[derive(Copy, Clone)] struct Complex { real: Coordinate, imag: Coordinate, } fn main() { }
Pokus o překlad se v tomto případě nepovede:
error[E0204]: the trait `Copy` may not be implemented for this type --> 67_copy_trait_impossible.rs:5:10 | 5 | #[derive(Copy, Clone)] | ^^^^ field `real` does not implement `Copy` 67_copy_trait_impossible.rs:5:10: 5:14 note: in this expansion of #[derive(Copy)] (defined in 67_copy_trait_impossible.rs) error: aborting due to previous error
Je tomu tak z toho prostého důvodu, protože datová struktura Complex je sice klonovatelná, ale její položky už nikoli, což je jeden z předpokladů úspěšného vytvoření úplné kopie objektu. Náprava je jednoduchá – vlastnosti Copy a Clone deklarujeme i pro strukturu Coordinate:
#[derive(Copy, Clone)] struct Coordinate { value: f32 } #[derive(Copy, Clone)] struct Complex { real: Coordinate, imag: Coordinate, } fn main() { }
Důvod, proč překlad původní datové struktury:
#[derive(Copy, Clone)] struct Complex { real: f32, imag: f32, }
proběhl bez chybových hlášení spočívá v tom, že základní datové typy, včetně f32, rys Clone implementují.
16. Odkazy na Internetu
- 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 - 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