Hlavní navigace

Práce s vektory v programovacím jazyku Rust

9. 2. 2017
Doba čtení: 22 minut

Sdílet

 Autor: Rust project
Primitivní datový typ pole (array), s nímž jsme se seznámili minule, neumožňuje při běhu programu přidávat či ubírat prvky. Pokud je takové chování zapotřebí, lze namísto polí použít objekt typu Vec neboli vektor.

Obsah

1. Vytvoření nového vektoru makrem vec!

2. Měnitelný a neměnitelný vektor

3. Indexování prvků vektoru a typ usize

4. Získání iterátoru pro vektory a průchod všemi prvky vektoru

5. Řezy (slice) vektoru

6. Modifikace prvků vektoru přes slice

7. Operace push a pop

8. Nastavení kapacity vektoru, zmenšení vektoru na základě skutečného počtu prvků

9. Vytvoření nového vektoru z iterátoru

10. Vytvoření nového vektoru z kolekce

11. Vytvoření nového vektoru z objektu typu range

12. Vektor obsahující další vektory

13. Vektor obsahující pole

14. Pole obsahující vektory

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

16. Odkazy na Internetu

1. Vytvoření nového vektoru makrem vec!

Jak jsme se dozvěděli minule, obsahuje programovací jazyk Rust podporu pro pole (array), která jsou zde dokonce považována za primitivní datový typ. Připomeňme si, že pole se může alokovat na zásobníku, překladač potřebuje znát počet prvků pole již při překladu, z polí lze vytvářet řezy (slices), délka polí se zjišťuje metodou len() a pro pole lze mj. získat i iterátor, a to konkrétně metodou iter().

V mnoha případech je však zapotřebí používat takové homogenní datové struktury, u nichž je možné v době běhu programu měnit počet prvků, tj. prvky přidávat a ubírat (navíc by všechny operace měly mít složitost O(1)). Takovou strukturou jsou v programovacím jazyku Rust objekty typu Vec nazývané vektory. Interně Vec obsahuje několik atributů, především kapacitu vektoru, počet prvků vektoru (může být menší než kapacita) a taktéž ukazatel na vlastní prvky, které jsou však umístěny na haldě (dokonce musí být na haldě, protože překladač nezná délku vektoru). Vektory lze vytvořit konstruktorem Vec::new(), ovšem častěji uvidíme použití makra vec!. Podívejme se, jak lze vytvořit vektor s pěti prvky, jejichž typ překladač použije pro odvození typu vektoru. V příkladu je ukázáno i indexování prvků a použití metody len() pro zjištění aktuální délky vektoru:

fn main() {
    let vector = vec![1, 2, 3, 4, 5];
 
    println!("vector has {} items", vector.len());
 
    for i in 0..vector.len() {
        println!("item #{} = {}", i+1, vector[i]);
    }
}

Výsledek běhu programu:

vector has 5 items
item #1 = 1
item #2 = 2
item #3 = 3
item #4 = 4
item #5 = 5

Alternativně je možné všechny prvky nastavit na stejnou hodnotu, a to použitím stejné syntaxe, jakou známe z článku o polích – [hodnota;počet_prvků]:

fn main() {
    let vector = vec![1; 10];
 
    println!("vector has {} items", vector.len());
 
    for i in 0..vector.len() {
        println!("item #{} = {}", i+1, vector[i]);
    }
}

Výsledek běhu programu:

vector has 10 items
item #1 = 1
item #2 = 1
item #3 = 1
item #4 = 1
item #5 = 1
item #6 = 1
item #7 = 1
item #8 = 1
item #9 = 1
item #10 = 1

2. Měnitelný a neměnitelný vektor

Podobně jako u polí platí, že vektory jsou implicitně neměnitelné (immutable), což znamená, že ve chvíli, kdy je vektor vytvořen a přiřazen do proměnné, už nelze měnit hodnoty jeho prvků či počet prvků. Z tohoto důvodu je následující příklad nekorektní:

fn main() {
    let vector = vec![1; 10];
 
    println!("vector has {} items", vector.len());
 
    print!("[");
    for i in 0..vector.len() {
        print!("{} ", vector[i]);
    }
    println!("]");
 
    vector[5] = 100;
 
    print!("[");
    for i in 0..vector.len() {
        print!("{} ", vector[i]);
    }
    println!("]");
}

Překlad skončí s chybou nalezenou na řádku, kde se pokoušíme modifikovat prvek vektoru:

error: cannot borrow immutable local variable `vector` as mutable
  --> 155_immutable_vector.rs:12:5
   |
2  |     let vector = vec![1; 10];
   |         ------ use `mut vector` here to make mutable
...
12 |     vector[5] = 100;
   |     ^^^^^^ cannot borrow mutably
 
error: aborting due to previous error

Oprava programu je snadná – postačuje použít nám již známý modifikátor mut, a to následovně:

fn main() {
    let mut vector = vec![1; 10];
 
    println!("vector has {} items", vector.len());
 
    print!("[");
    for i in 0..vector.len() {
        print!("{} ", vector[i]);
    }
    println!("]");
 
    vector[5] = 100;
 
    print!("[");
    for i in 0..vector.len() {
        print!("{} ", vector[i]);
    }
    println!("]");
}

Nyní je již možné příklad přeložit a spustit s následujícím výsledkem (prvek byl skutečně modifikován):

vector has 10 items
[1 1 1 1 1 1 1 1 1 1 ]
[1 1 1 1 1 100 1 1 1 1 ]

3. Indexování prvků vektoru a typ usize

Přístup k prvkům vektorů s využitím celočíselného indexu je jednoduchý a už jsme si ho vlastně ukázali v předchozích příkladech. Takže jen pro ilustraci, jak lze přistoupit ke třetímu prvku (s indexem rovným dvěma):

fn main() {
    let vector = vec![1, 2, 3, 4, 5];
    let index = 2;
 
    let item = vector[index];
 
    println!("vector[5] == {}", item);
}

Předchozí příklad byl přeložen v pořádku, ale pokud se pokusíme o použití celočíselné konstanty některého z typů integer (například i32, tedy 32bitový integer se znaménkem), dojde při překladu k chybě:

fn main() {
    let vector = vec![1, 2, 3, 4, 5];
    let index = 2i32;
 
    let item = vector[index];
 
    println!("vector[5] == {}", item);
}

Chyba při překladu:

error[E0277]: the trait bound `std::vec::Vec<{integer}>: std::ops::Index<i32>` is not satisfied
 --> 158_vector_integer_index.rs:5:16
  |
5 |     let item = vector[index];
  |                ^^^^^^^^^^^^^
  |
  = note: the type `std::vec::Vec<{integer}>` cannot be indexed by `i32`
 
error: aborting due to previous error

Podobně dopadneme ve chvíli, kdy specifikujeme typ proměnné obsahující index (což je vlastně to samé):

fn main() {
    let vector = vec![1, 2, 3, 4, 5];
    let index:i32 = 2;
 
    let item = vector[index];
 
    println!("vector[5] == {}", item);
}

Problém spočívá v tom, že vektory implementují trait Index<usize>, aby vůbec bylo možné použít syntaxi vektor[index_prvku]. A právě kvůli tomu je nutné pro indexaci použít hodnotu typu usize (dokonce ani není možné použít isize). Následující dvojice příkladů je tedy korektní:

fn main() {
    let vector = vec![1, 2, 3, 4, 5];
    let index:usize = 2;
 
    let item = vector[index];
 
    println!("vector[5] == {}", item);
}

Povšimněte si, že u číselných proměnných lze jejich typ přesně specifikovat zápisem typu proměnné ihned za číselnou konstantou, která je do proměnné přiřazována:

fn main() {
    let vector = vec![1, 2, 3, 4, 5];
    let index = 2usize;
 
    let item = vector[index];
 
    println!("vector[5] == {}", item);
}

Poznámka: vektory ve skutečnosti implementují dva podobně pojmenované traity Index a IndexMut. První trait se používá ve chvíli, kdy se prvek čte, druhý se používá při zápisu (modifikaci, mutaci):

let x = vector[10];
vector[20] = x;

4. Získání iterátoru pro vektory a průchod všemi prvky vektoru

Podobně jako u polí lze i pro vektory získat iterátor metodou iter(). Idiomatický zápis smyčky, v níž se prochází všemi prvky vektoru, vypadá takto:

fn main() {
    let vector = vec![1, 2, 3, 4, 5];
 
    println!("vector has {} items", vector.len());
 
    for item in vector.iter() {
        println!("{}", item);
    }
}

Vektory mají s poli společnou i další vlastnost – iterátor lze získat zápisem &vektor. Přitom nedojde ke změně vlastníka vektoru:

fn main() {
    let vector = vec![1, 2, 3, 4, 5];
 
    println!("vector has {} items", vector.len());
 
    for item in &vector {
        println!("{}", item);
    }
}

Zatímco u polí byl předchozí zápis omezen pouze na pole s maximální velikostí 32 prvků, u vektorů tomu tak není, o čemž se lze snadno přesvědčit:

fn main() {
    let vector = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
                      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
                      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
                      1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
 
    println!("vector has {} items", vector.len());
 
    for item in &vector {
        println!("{}", item);
    }
}

5. Řezy (slice) vektoru

Z vektorů je možné, podobně jako u polí, získat „řezy“ zápisem vektor[dolní_mez..horní_mez], přičemž platí, že v řezu je prvním prvkem prvek s indexem dolní_mez a posledním prvkem prvek s indexem horní_mez-1 – teoreticky by tedy zápis řezu měl vypadat takto: [dolní_mez..horní_mez), to by však pravděpodobně zmátlo textové editory, takže se tento zápis nepoužívá. Podívejme se na jednoduchý příklad získání řezu z desetiprvkového vektoru:

fn main() {
    let vector = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
 
    println!("vector has {} items", vector.len());
 
    let slice = vector[3..7];
 
    println!("slice has {} items", slice.len());
 
    for item in slice {
        println!("{}", item);
    }
}

Překlad tohoto příkladu se však nepovede:

error[E0277]: the trait bound `[{integer}]: std::marker::Sized` is not satisfied
 --> 165_slice_incorrect.rs:6:9
  |
6 |     let slice = vector[3..7];
  |         ^^^^^
  |
  = note: `[{integer}]` does not have a constant size known at compile-time
  = note: all local variables must have a statically known size

Chybové hlášení je sice poněkud kryptické, ale říká nám, že překladač není schopen odvodit velikost výsledné struktury. Pomůže nám maličkost (opět stejně jako u polí) – použít &, takže řez bude ukazovat do existujícího vektoru:

fn main() {
    let vector = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
 
    println!("vector has {} items", vector.len());
 
    let slice = &vector[3..7];
 
    println!("slice has {} items", slice.len());
 
    for item in slice {
        println!("{}", item);
    }
}

Výsledek běhu programu:

vector has 10 items
slice has 4 items
4
5
6
7

6. Modifikace prvků vektoru přes slice

V diskuzipředchozího článku zazněl dotaz (a správné odpovědi), jak je to s modifikací původního pole přes řez. Podívejme se nyní na toto téma z pohledu vektorů. Například získáme řez z desetiprvkového vektoru a budeme se snažit změnit hodnotu prvku přes tento řez:

fn main() {
    let mut vector = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
 
    println!("vector has {} items", vector.len());
 
    let mut slice = &vector[3..7];
    slice[1] = 100;
 
    for item in &vector {
        println!("{}", item);
    }
}

Proměnná slice je sice označena modifikátorem mut, to ovšem znamená, že do proměnné lze přiřadit jiný řez a nikoli to, že obsah samotného řezu (a tedy i vektoru) je měnitelný:

error: cannot assign to immutable indexed content `slice[..]`
 --> 167_modify_via_slice_incorrect.rs:7:5
  |
7 |     slice[1] = 100;
  |     ^^^^^^^^^^^^^^
 
error: aborting due to previous error

Následující příklad již sice správně používá &mut (tj. prvek vektoru lze přes řez změnit), ovšem nesmíme zapomenout na to, že se v tomto případě změní i vlastník vektoru, což způsobí problém při překladu programové smyčky:

fn main() {
    let mut vector = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
 
    println!("vector has {} items", vector.len());
 
    let slice = &mut vector[3..7];
    slice[1] = 100;
 
    for item in &vector {
        println!("{}", item);
    }
}
error[E0502]: cannot borrow `vector` as immutable because it is also borrowed as mutable
  --> 168_modify_via_slice_incorrect2.rs:9:18
   |
6  |     let slice = &mut vector[3..7];
   |                      ------ mutable borrow occurs here
...
9  |     for item in &vector {
   |                  ^^^^^^ immutable borrow occurs here
...
12 | }
   | - mutable borrow ends here
 
error: aborting due to previous error

Jedno z možných řešení (či možná lépe řečeno obejití) tohoto problému spočívá v omezení viditelnosti proměnné slice pomocí programového bloku:

fn main() {
    let mut vector = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
 
    println!("vector has {} items", vector.len());
 
    {
        let slice = &mut vector[3..7];
 
        slice[1] = 100;
    }
 
    for item in &vector {
        println!("{}", item);
    }
}

Výsledek běhu programu:

vector has 10 items
1
2
3
4
100
6
7
8
9
10

7. Operace push a pop

V úvodní kapitole jsme si řekli, že počet prvků vektoru může růst či klesat, protože jsou podporovány dvě operace nazvané push() a pop(), které dokážou na konec vektoru přidat nový prvek popř. tento prvek odebrat. Je to umožněno díky tomu, že prvky vektoru jsou umístěny na haldu a vektor může v případě potřeby růst. Pokud má vektor dostatečnou kapacitu, mají tyto operace složitost O(1), což je samozřejmě příjemné. Podívejme se na velmi jednoduchý příklad použití:

fn main() {
    let mut vector = vec![];
 
    for i in 0..10 {
        vector.push(2*i);
    }
 
    println!("vector has {} items", vector.len());
 
    for item in &vector {
        println!("{}", item);
    }
 
    for _ in 0..5 {
        vector.pop();
    }
 
    println!("-------------------------");
 
    for item in &vector {
        println!("{}", item);
    }
}

Výsledek běhu programu:

vector has 10 items
0
2
4
6
8
10
12
14
16
18
-------------------------
0
2
4
6
8

8. Nastavení kapacity vektoru, zmenšení vektoru na základě skutečného počtu prvků

Aby bylo přidávání prvků na konec vektoru efektivní, je zapotřebí zajistit, aby měl vektor potřebnou kapacitu pro přidávání prvků. Pokud totiž bude kapacita vektoru překročena, dojde k jejímu zvětšení, což je již složitější operace, které se většinou budeme chtít vyhnout. V případě, že je počet prvků vektoru dopředu alespoň přibližně známý, je možné vektor vytvořit již s potřebnou kapacitou, a to konkrétně konstruktorem Vec::with_capacity(počet_pře­dalokovaných_prvků). Pokud zadáte příliš velkou počáteční kapacitu, bude se zbytečně alokovat paměť na haldě, na druhou stranu se zaručí, že se vektor nebude realokovat. Kapacitu je možné zjistit snadno metodou capacity(). Tato hodnota by měla být větší nebo rovna hodnotě vrácené metodou len():

fn main() {
    let mut vector = Vec::with_capacity(10);
 
    println!("vector has capacity for {} items", vector.capacity());
    println!("vector has {} items", vector.len());
 
    for i in 0..10 {
        vector.push(2*i);
    }
 
    println!("vector has {} items", vector.len());
 
    for item in &vector {
        println!("{}", item);
    }
 
    for _ in 0..5 {
        vector.pop();
    }
 
    println!("-------------------------");
 
    for item in &vector {
        println!("{}", item);
    }
}

Výsledek běhu programu:

vector has capacity for 10 items
vector has 0 items
vector has 10 items
0
2
4
6
8
10
12
14
16
18
-------------------------
0
2
4
6
8

V případě potřeby lze kapacitu vektoru kdykoli zmenšit na nejmenší možnou velikost odpovídající počtu skutečně vložených prvků. Změnu velikosti zajistí metoda shrink_to_fit(), která však provádí realokaci, a proto ji většinou není vhodné volat příliš často:

fn main() {
    let mut vector = Vec::with_capacity(10);
 
    println!("vector has capacity for {} items", vector.capacity());
    println!("vector has {} items", vector.len());
 
    for i in 0..10 {
        vector.push(2*i);
    }
 
    println!("vector has {} items", vector.len());
 
    for item in &vector {
        println!("{}", item);
    }
 
    for _ in 0..5 {
        vector.pop();
    }
 
    println!("-------------------------");
 
    vector.shrink_to_fit();
 
    println!("vector has capacity for {} items", vector.capacity());
    println!("vector has {} items", vector.len());
 
    for item in &vector {
        println!("{}", item);
    }
}

Výsledek ukazuje, že se kapacita vektoru skutečně zmenšila na pouhých pět prvků:

vector has capacity for 10 items
vector has 0 items
vector has 10 items
0
2
4
6
8
10
12
14
16
18
-------------------------
vector has capacity for 5 items
vector has 5 items
0
2
4
6
8

U vektorů vytvořených konstruktorem Vec::with_capacity() dokáže překladač odvodit typ prvků z prvního přiřazení, což mj. znamená, že následující příklad nebude přeložen, protože se do vektoru snažíme uložit prvky nekompatibilních typů:

fn main() {
    let mut vector = Vec::with_capacity(10);
 
    vector.push(10.0);
    vector.push(10);
 
    println!("vector has {} items", vector.len());
}

Samozřejmě není problém zapsat typ prvků explicitně:

fn main() {
    let mut vector : Vec<i8> = Vec::with_capacity(10);
 
    vector.push(10);
    vector.push(100);
    vector.push(1000);
 
    println!("vector has {} items", vector.len());
}

Zde překladač pouze vypíše varování, že se snažíme uložit příliš velkou hodnotu 1000 do prvku typu i8.

9. Vytvoření nového vektoru z iterátoru

V případě potřeby je možné vektor vytvořit z iterátoru, a to metodou from_iter(). V následujícím příkladu je iterátor tvořen sekvencí čísel od 1 do 9, v praxi však samozřejmě nejsme omezeni na to, jak a čím je iterátor vytvořen (typ generovaných prvků je známý při překladu):

use std::iter::FromIterator;
 
fn main() {
    let vector = Vec::from_iter(1..10);
 
    println!("vector has {} items", vector.len());
 
    for item in &vector {
        println!("{}", item);
    }
}

10. Vytvoření nového vektoru z kolekce

Velmi užitečný je i další způsob převedení iterátoru či kolekce na vektor, a to konkrétně metodou collect(). Nejprve se podívejme na převod pole→iterátor→vektor:

fn main() {
    let array  = [1, 2, 3, 4, 5];
    let vector:Vec<_> = array.iter().collect();
 
    println!("vector has {} items", vector.len());
  
    for item in &vector {
        println!("{}", item);
    }
}

Připomeňme si, že v Rustu je možné použít i funkce vyššího řádu, například map, filter, take či take_while, takže lze převody mezi různými kolekcemi vyřešit funkcionálním stylem, například:

fn main() {
    let array  = [1, 2, 3, 4, 5];
    let vector:Vec<_> = array.iter().map(|x| x*2).collect();
 
    println!("vector has {} items", vector.len());
 
    for item in &vector {
        println!("{}", item);
    }
}

Příklad použití kombinace take+filter+map s následným převodem na vektor:

fn main() {
    let array  = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
    let vector:Vec<_> = array.iter()
                             .take(10)
                             .filter(|&x| x % 3 ==0)
                             .map(|&x| x*2)
                             .collect();
 
    println!("vector has {} items", vector.len());
 
    for item in &vector {
        println!("{}", item);
    }
}

Samozřejmě je možné explicitně specifikovat jak typ vstupní kolekce, tak i typ prvků vektoru:

fn main() {
    let array:[i32;15]  = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
    let vector:Vec<i32> = array.iter()
                             .take(10)
                             .filter(|&x| x % 3 ==0)
                             .map(|&x| x*2)
                             .collect();
 
    println!("vector has {} items", vector.len());
 
    for item in &vector {
        println!("{}", item);
    }
}

Výsledkem bude:

vector has 3 items
6
12
18

Tedy prvky dělitelné třemi z prvních deseti prvků pole, které jsou následně vynásobeny dvěma.

11. Vytvoření nového vektoru z objektu typu range

Vektor je možné vytvořit i z objektu typu range, zadaný rozsah však musíme uzavřít do kulatých závorek (záležitost syntaxe):

fn main() {
    let vector:Vec<_> = (0..10).collect();
 
    println!("vector has {} items", vector.len());
 
    for item in &vector {
        println!("{}", item);
    }

Povšimněte si, že jak s vektorem, tak i s poli je možné pracovat naprosto stejným způsobem, pokud k nim přistupujeme přes referenci či řez:

fn print_slice(slice :&[i32]) {
    print!("[");
    for i in slice {
        print!("{} ", i);
    }
    println!("]");
}
 
fn main() {
    let array1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
    let array2 = [1; 10];
    let vector:Vec<_> = (0..10).collect();
 
    print_slice(&array1);
    print_slice(&array2);
    print_slice(&vector);
}

Výsledek běhu programu:

[0 1 2 3 4 5 6 7 8 9 ]
[1 1 1 1 1 1 1 1 1 1 ]
[0 1 2 3 4 5 6 7 8 9 ]

12. Vektor obsahující další vektory

Poměrně často se setkáme s požadavkem, aby vektor obsahoval jako své prvky další vektory. Díky typovému systému programovacího jazyka Rust je to samozřejmě možné, protože vektory jsou plnohodnotným datovým typem. Výsledné „dvourozměrné vektory“ se od dvourozměrných polí odlišují jak způsobem alokace (zásobník versus halda), tak i tím, že výsledná datová struktura složená z vektorů nemusí být obdélníková, tj. vektory mohou mít rozdílnou a navíc i v čase proměnnou délku. Podívejme se, jak se deklaruje typ proměnné s vektorem obsahujícím další vektory:

let vec2:Vec<Vec<typ_prvků>>

V některých případech se můžeme spolehnout na typovou inferenci a typ prvků explicitně neudávat (což použijeme v demonstračním příkladu):

let vec2:Vec<Vec<_>>

Při přístupu k prvkům lze použít běžné indexování:

println!("{}", vec2[2][1])

V příkladu namísto explicitního indexování použijeme iteraci přes všechny prvky vektoru. Víme již, že prvky jsou taktéž vektory a i pro ně lze získat iterátor, takže výpis „vektoru vektorů“ je vlastně velmi jednoduchý:

fn main() {
    let vec2:Vec<Vec<_>> = vec![vec![1,2,3],
                                vec![4],
                                vec![5,6,7,8,]];
 
    println!("vector has {} items", vec2.len());
 
    for vec1 in vec2.iter() {
        for i in vec1.iter() {
            print!("{} ", i);
        }
        println!("");
    }
 
}

Výsledek běhu programu:

vector has 3 items
1 2 3
4
5 6 7 8

13. Vektor obsahující pole

Možné jsou samozřejmě i další kombinace, například vektor obsahující pole. V takovém případě je nutné správně zapsat datový typ prvků vektoru, protože překladač Rustu vyžaduje znalost typů a počtu prvků polí vkládaných do vektoru. Připomeňme si z předchozího článku, že typ pole se zapisuje [typ_prvku;počet_prvků]. V následujícím demonstračním příkladu je zajímavé, že samotné vnořené programové smyčky jsou vlastně totožné s předchozím příkladem, protože iterátory lze použít jak u polí, tak i u vektorů (a samozřejmě nejenom pro tyto datové struktury):

fn main() {
    let vec2:Vec<[i32;3]> = vec![[1,2,3],
                                 [4,5,6],
                                 [7,8,9]];
 
    println!("vector has {} items", vec2.len());
 
    for array in vec2.iter() {
        for i in array.iter() {
            print!("{} ", i);
        }
        println!("");
    }
}

Výsledek běhu programu:

vector has 3 items
1 2 3
4 5 6
7 8 9

14. Pole obsahující vektory

Poslední možnou kombinací je pole obsahující vektory (první kombinaci – pole polí – jsme si popsali již minule). Vzhledem k tomu, že již víme, že typ pole se zapisuje formou [typ_prvku;počet_prvků], nebude pro nás žádným problémem za typ_prvku dosadit například Vec<i32> atd. Opět si povšimněte toho, že výpis obsahu takto vytvořené datové struktury je zcela totožný, jako v obou předchozích příkladech:

fn main() {
    let array:[Vec<i32>;4] = [vec![1],
                              vec![2,3],
                              vec![4,5,6],
                              vec![7,8,9,0]];
 
    println!("array has {} items", array.len());
 
    for vec1 in array.iter() {
        for i in vec1.iter() {
            print!("{} ", i);
        }
        println!("");
    }
}

Takto vytvořené pole vlastně obsahuje jednoduché objekty (struktury) s konstantní velikostí, které obsahují ukazatel na vlastní prvky vektoru umístěné na haldě.

Cloud23

Výsledek běhu programu:

array has 4 items
1
2 3
4 5 6
7 8 9 0

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

Všechny dnes popisované demonstrační příklady byly, podobně jako ve všech předchozích částech tohoto seriálu, uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/pre­sentations. Příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý repositář:

Příklad Odkaz
153_new_vector.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/153_new_vector.rs
154_new_vector_repeat_item.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/154_new_vector_repeat_i­tem.rs
155_immutable_vector.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/155_immutable_vector.rs
156_mutable_vector.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/156_mutable_vector.rs
157_vector_index.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/157_vector_index.rs
158_vector_integer_index.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/158_vector_integer_index­.rs
159_vector_integer_index2.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/159_vector_integer_index2­.rs
160_vector_usize_index.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/160_vector_usize_index.rs
161_vector_usize_index2.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/161_vector_usize_index2­.rs
162_foreach_vector.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/162_foreach_vector.rs
163_foreach_ref.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/163_foreach_ref.rs
164_foreach_large_vector.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/164_foreach_large_vector­.rs
165_slice_incorrect.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/165_slice_incorrect.rs
166_slice_correct.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/166_slice_correct.rs
167_modify_via_slice_incorrect.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/167_modify_via_slice_in­correct.rs
168_modify_via_slice_incorrect2.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/168_modify_via_slice_in­correct2.rs
169_modify_via_slice_correct.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/169_modify_via_slice_co­rrect.rs
170_vector_push_pop.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/170_vector_push_pop.rs
171_vector_push_pop2.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/171_vector_push_pop2.rs
172_vec_with_capacity.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/172_vec_with_capacity.rs
173_vector_shrink_to_fit.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/173_vector_shrink_to_fit­.rs
174_vector_type_incorrect.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/174_vector_type_incorrec­t.rs
175_vector_explicit_type.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/175_vector_explicit_type­.rs
176_vector_from_iter.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/176_vector_from_iter.rs
177_iter_collection_vector.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/177_iter_collection_vec­tor.rs
178_iter_map_collection_vector.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/178_iter_map_collection_vec­tor.rs
179_iter_to_vector_pipe.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/179_iter_to_vector_pipe­.rs
180_full_type_info.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/180_full_type_info.rs
181_full_type_info.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/181_full_type_info.rs
182_vector_from_range.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/182_vector_from_range.rs
183_vector_array_slice.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/183_vector_array_slice.rs
184_vector_of_vectors.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/184_vector_of_vectors.rs
185_vector_of_arrays.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/185_vector_of_arrays.rs
186_array_of_vectors.rs https://github.com/tisnik/pre­sentations/blob/master/rus­t/186_array_of_vectors.rs

16. Odkazy na Internetu

  1. Module std::vec
    https://doc.rust-lang.org/nightly/std/vec/index.html
  2. Primitive Type isize
    https://doc.rust-lang.org/nightly/std/primi­tive.isize.html
  3. Primitive Type usize
    https://doc.rust-lang.org/nightly/std/primi­tive.usize.html
  4. Primitive Type array
    https://doc.rust-lang.org/nightly/std/primi­tive.array.html
  5. Module std::slice
    https://doc.rust-lang.org/nightly/std/slice/
  6. Rust by Example: 2.3 Arrays and Slices
    http://rustbyexample.com/pri­mitives/array.html
  7. What is the difference between Slice and Array (stackoverflow)
    http://stackoverflow.com/qu­estions/30794235/what-is-the-difference-between-slice-and-array
  8. Learning Rust With Entirely Too Many Linked Lists
    http://cglab.ca/~abeinges/blah/too-many-lists/book/
  9. Testcase: linked list
    http://rustbyexample.com/cus­tom_types/enum/testcase_lin­ked_list.html
  10. Operators and Overloading
    https://doc.rust-lang.org/book/operators-and-overloading.html
  11. Module std::ops
    https://doc.rust-lang.org/std/ops/index.html
  12. Module std::cmp
    https://doc.rust-lang.org/std/cmp/index.html
  13. Trait std::ops::Add
    https://doc.rust-lang.org/stable/std/ops/trait.Add.html
  14. Trait std::ops::AddAssign
    https://doc.rust-lang.org/std/ops/trait.AddAssign.html
  15. Trait std::ops::Drop
    https://doc.rust-lang.org/std/ops/trait.Drop.html
  16. Trait std::cmp::Eq
    https://doc.rust-lang.org/std/cmp/trait.Eq.html
  17. Struct std::boxed::Box
    https://doc.rust-lang.org/std/boxed/struct.Box.html
  18. Explore the ownership system in Rust
    https://nercury.github.io/rus­t/guide/2015/01/19/ownership­.html
  19. Rust's ownership and move semantic
    http://www.slideshare.net/sa­neyuki/rusts-ownership-and-move-semantics
  20. Trait std::marker::Copy
    https://doc.rust-lang.org/stable/std/marker/tra­it.Copy.html
  21. Trait std::clone::Clone
    https://doc.rust-lang.org/stable/std/clone/tra­it.Clone.html
  22. The Stack and the Heap
    https://doc.rust-lang.org/book/the-stack-and-the-heap.html
  23. Rust Compare: Pointers & References
    http://www.rust-compare.com/site/pointers.html
  24. Rust Compare: Parameters
    http://www.rust-compare.com/site/params.html
  25. Why does this compile? Automatic dereferencing?
    https://users.rust-lang.org/t/why-does-this-compile-automatic-dereferencing/2183
  26. Understanding Pointers, Ownership, and Lifetimes in Rust
    http://koerbitz.me/posts/Understanding-Pointers-Ownership-and-Lifetimes-in-Rust.html
  27. Rust lang series episode #25 — pointers (#rust-series)
    https://steemit.com/rust-series/@jimmco/rust-lang-series-episode-25-pointers-rust-series
  28. Rust – home page
    https://www.rust-lang.org/en-US/
  29. Rust – Frequently Asked Questions
    https://www.rust-lang.org/en-US/faq.html
  30. Destructuring and Pattern Matching
    https://pzol.github.io/get­ting_rusty/posts/20140417_des­tructuring_in_rust/
  31. The Rust Programming Language
    https://doc.rust-lang.org/book/
  32. Rust (programming language)
    https://en.wikipedia.org/wi­ki/Rust_%28programming_lan­guage%29
  33. Go – home page
    https://golang.org/
  34. Stack Overflow – Most Loved, Dreaded, and Wanted language
    https://stackoverflow.com/re­search/developer-survey-2016#technology-most-loved-dreaded-and-wanted
  35. 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/
  36. Rust vs Go: My experience
    https://www.reddit.com/r/go­lang/comments/21m6jq/rust_vs_go_my_ex­perience/
  37. Friends of Rust (Organizations running Rust in production)
    https://www.rust-lang.org/en-US/friends.html
  38. Rust programs versus C++ g++
    https://benchmarksgame.ali­oth.debian.org/u64q/compa­re.php?lang=rust&lang2=gpp
  39. Další benchmarky (nejedná se o reálné příklady „ze života“)
    https://github.com/kostya/benchmarks
  40. Go na Redditu
    https://www.reddit.com/r/golang/
  41. Rust vs. Go
    http://vschart.com/compare/rust/vs/go-language
  42. Abstraction without overhead: traits in Rust
    https://blog.rust-lang.org/2015/05/11/traits.html
  43. Method Syntax
    https://doc.rust-lang.org/book/method-syntax.html
  44. Traits in Rust
    https://doc.rust-lang.org/book/traits.html
  45. Functional Programming in Rust – Part 1 : Function Abstraction
    http://blog.madhukaraphatak­.com/functional-programming-in-rust-part-1/
  46. 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
  47. Chytré ukazatele (moderní verze jazyka C++) [MSDN]
    https://msdn.microsoft.com/cs-cz/library/hh279674.aspx
  48. UTF-8 Everywhere
    http://utf8everywhere.org/
  49. Rust by Example
    http://rustbyexample.com/
  50. Rust oficiálně ve Fedoře
    https://mojefedora.cz/rust-oficialne-ve-fedore/
  51. Resource acquisition is initialization
    https://en.wikipedia.org/wi­ki/Resource_acquisition_is_i­nitialization
  52. TIOBE index (October 2016)
    http://www.tiobe.com/tiobe-index/
  53. Porovnání Go, D a Rustu na OpenHubu:
    https://www.openhub.net/lan­guages/compare?language_na­me[]=-1&language_name[]=-1&language_name[]=dmd&lan­guage_name[]=golang&langu­age_name[]=rust&language_na­me[]=-1&measure=commits
  54. String Types in Rust
    http://www.suspectsemantic­s.com/blog/2016/03/27/str­ing-types-in-rust/
  55. Trait (computer programming)
    https://en.wikipedia.org/wi­ki/Trait_%28computer_program­ming%29
  56. Type inference
    https://en.wikipedia.org/wi­ki/Type_inference

Autor článku

Pavel Tišnovský vystudoval VUT FIT a v současné době pracuje ve společnosti Red Hat, kde vyvíjí nástroje pro OpenShift.io.