Vectors

See this chapter on YouTube

Sebagaimana kita memiliki &str dan String, kita pun memiliki array dan juga vector. Array lebih cepat namun kegunaannya sedikit. Sedangkan vector sangat lambat, tapi memiliki banyak kegunaan. (Tentu saja, Rust selalu sangat cepat, sehingga vector tidaklah lambat, hanya saja ia lebih lambat jika dibandingkan array.) Typenya adalah Vec, dan Anda bisa menyebutnya cukup dengan "vec".

Ada 2 cara untuk mendeklarasikan vector. Yang pertama, mirip seperti String menggunakan new:

fn main() {
    let name1 = String::from("Windy");
    let name2 = String::from("Gomesy");

    let mut my_vec = Vec::new();
    // Jika kita menuliskan programnya hanya sampai sini dan menjalankannya, compiler akan memberikan error.
    // Karena ia tidak tahu type dari vec.

    my_vec.push(name1); // Now it knows: it's Vec<String>
    my_vec.push(name2);
}

Anda bisa melihat bahwa Vec selalu memiliki sesuatu di dalamnya, dan itulah kegunaan dari <> (angle brackets). Vec<String> adalah vector dengan 1 atau banyak String. Anda juga bisa menggunakan lebih dari 1 type di dalamnya. Contohnya:

  • Vec<(i32, i32)> adalah Vec dimana setiap itemnya adalah tuple: (i32, i32).
  • Vec<Vec<String>> adalah Vec dimana setiap itemnya adalah Vec dari String. Sebagai contoh, anggap saja Anda menginginkan untuk menyimpan daftar nama-nama buku favorit Anda ke dalam Vec<String>. Kemudian, Anda lakukan itu lagi dengan nama-nama buku lainnya, dan Anda mendapatkan Vec<String> lagi. Untuk menyimpan kedua daftar buku-buku ini, Anda tentunya akan meletakkannya ke dalam Vec lagi dan ia akan menjadi Vec<Vec<String>>.

Alih-alih menggunakan .push() untuk membuat Rust memilihkan typenya, Anda bisa juga langsung mendeklarasikan typenya.

fn main() {
    let mut my_vec: Vec<String> = Vec::new(); // Compiler mengetahui typenya
                                              // maka tidak akan ada error pada code ini.
}

Anda bisa melihat bahwa item-item yang ada di dalam vectors harus memiliki type yang sama.

Cara mudah lainnya untuk membuat suatu vector adalah dengan menggunakan macro vec!. Ini mirip seperti pendeklarasian array, namun dengan vec! di depannya.

fn main() {
    let mut my_vec = vec![8, 10, 10];
}

Typenya adalah Vec<i32>. Anda bisa menyebutnya sebagai "Vec dari i32". Dan Vec<String> adalah "Vec dari string". Dan Vec<Vec<String>> adalah "Vec dari vec dari string".

Anda juga bisa melakukan slice pada vector, sama seperti yang kita lakukan pada array.

fn main() {
    let vec_of_ten = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    // Everything is the same as above except we added vec!.
    let three_to_five = &vec_of_ten[2..5];
    let start_at_two = &vec_of_ten[1..];
    let end_at_five = &vec_of_ten[..5];
    let everything = &vec_of_ten[..];

    println!("Three to five: {:?},
start at two: {:?}
end at five: {:?}
everything: {:?}", three_to_five, start_at_two, end_at_five, everything);
}

Karena vec lebih lambat dibandingkan dengan array, kita bisa menggunakan suatu cara untuk membuatnya menjadi cepat. Vector memiliki kapasitas, yang artinya adalah ruang yang diberikan kepada vector. Di saat Anda push item baru ke dalam vector, ia akan mendekati dan terus mendekati ke batas kapasitasnya. Maka, jika item yang ter-push telah melebihi batas kapasitasnya, maka ia akan membuat kapasitasnya menjadi dua kali lipat dan menyalin semua item di dalamnya ke dalam ruang yang baru. Ini biasa disebut sebagai reallocation. Kita akan menggunakan method .capacity() untuk melihat kapasitas dari vector saat kita menambahkan item ke dalamnya.

Berikut contohnya:

fn main() {
    let mut num_vec = Vec::new();
    println!("{}", num_vec.capacity()); // 0 element: kapasitasnya 0
    num_vec.push('a'); // tambah 1 karakter
    println!("{}", num_vec.capacity()); // 1 element: kapasitasnya 4. Vec dengan 1 item selalu mulai dengan kapasitas 4
    num_vec.push('a'); // tambah satu lagi
    num_vec.push('a'); // tambah satu lagi
    num_vec.push('a'); // tambah satu lagi
    println!("{}", num_vec.capacity()); // 4 element: kapasitasnya tetap 4.
    num_vec.push('a'); // tambah satu lagi
    println!("{}", num_vec.capacity()); // cetak 8. Kita memiliki 5 element, namun ia melipatgandakan kapasitasnya yang semula 4 menjadi 8 untuk membuat ruang yang lebih
}

Hasil printnya adalah sebagai berikut:

0
4
4
8

Vector pada program di atas memiliki 2 reallocation: Dari 0 ke 4, dan dari 4 ke 8. Kita bisa membuatnya lebih cepat:

fn main() {
    let mut num_vec = Vec::with_capacity(8); // Kita langsung tulis bahwa program memerlukan kapasitas 8
    num_vec.push('a'); // tambah satu karakter
    println!("{}", num_vec.capacity()); // kapasitasnya 8
    num_vec.push('a'); // tambah satu lagi
    println!("{}", num_vec.capacity()); // kapasitasnya 8
    num_vec.push('a'); // tambah satu lagi
    println!("{}", num_vec.capacity()); // kapasitasnya 8.
    num_vec.push('a'); // tambah satu lagi
    num_vec.push('a'); // tambah satu lagi // Sekarang kita memiliki 5 element
    println!("{}", num_vec.capacity()); // kapasitasnya tetap 8
}

Vector pada program ini memiliki 0 reallocation, yang mana ini lebih baik. Jadi jika Anda berfikir bahwa Anda mengetahui seberapa banyak element yang Anda perlukan, Anda bisa menggunakan Vec::with_capacity() untuk membuatnya lebih cepat.

Anda juga harus mengingat bahwa Anda bisa menggunakan .into() untuk membuat &str menjadi String. Anda juga bisa menggunakannya untuk membuat array menjadi Vec. Anda harus memberi tahu kepada .into() bahwa Anda menginginkan Vec, tapi Anda tidak harus memilih type untuk Vecnya. Jika Anda tidak ingin memilih typenya, Anda bisa menulis Vec<_>.

fn main() {
    let my_vec: Vec<u8> = [1, 2, 3].into();
    let my_vec2: Vec<_> = [9, 0, 10].into(); // Vec<_> artinya "pilihkan type Vec-nya untuk saya"
                                             // Maka Rust akan memilihkan Vec<i32>
}