向量

YouTube 上觀看本章內容

就像我們有 &strString 一樣的方式,我們有陣列和向量(vector)。陣列的功能少了就快,向量的功能多了就慢。(當然,Rust 的速度一直都是非常快的,所以向量並不慢,只是比陣列慢一點)。型別被寫作 Vec,你也可以直接叫它 "vec"。

向量的宣告主要有兩種方式。一種像 String 使用 new:

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

    let mut my_vec = Vec::new();
    // 如果我們現在就跑程式,編譯器會給出錯誤。
    // 它不知道vec的型別。
    
    my_vec.push(name1); // 現在它知道了:它是Vec<String>
    my_vec.push(name2);
}

你可以看到 Vec 裡面總是有其他東西,這就是 <>(角括號)的作用。Vec<String>是有一或多個 String 的向量。你還可以在裡面有更多的型別。舉例來說:

  • Vec<(i32, i32)> 這個 Vec 的每個元素是元組(tuple):(i32, i32)
  • Vec<Vec<String>> 這個 Vec 裡面有包含 StringVec。假設說你想把你喜歡的書保存在 Vec<String>。然後你再拿另一本書重做一次,就會得到另一個 Vec<String>。為了保留這兩本書,你會把它們放入另一個 Vec 中,這就是 Vec<Vec<String>>

與其使用 .push() 讓 Rust 決定型別,不如直接宣告型別。

fn main() {
    let mut my_vec: Vec<String> = Vec::new(); // 編譯器知道型別
                                              // 所以沒有錯誤。
}

你可以看到,向量中的元素必須具有相同的型別。

建立向量的另一個簡單方法是使用 vec! 巨集。它看起來像一個陣列宣告,但前面有 vec!

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

型別是 Vec<i32>。你稱它為 "i32 的 Vec"。而 Vec<String> 是 "String 的 Vec"。Vec<Vec<String>> 是 "String 的 Vec 的 Vec"。

你也可以對一個向量進行切片,就像用在陣列一樣。

fn main() {
    let vec_of_ten = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    // 所有東西都和前面的陣列一樣,除了我們加上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);
}

因為向量比陣列慢,我們可以用一些方法讓它更快。向量都有容量(capacity),也就是給予向量使用的空間。當你在向量上推送一個新元素時,它會越來越接近容量。然後,如果你超過了容量,它將使其容量翻倍,並將元素複製到新的空間。這就是所謂的再分配(reallocation)。我們將使用名為 .capacity() 的方法,在我們向它新增元素時來查看向量的容量。

例如:

fn main() {
    let mut num_vec = Vec::new();
    println!("{}", num_vec.capacity()); // 0 個元素: 印出 0
    num_vec.push('a'); // 加人一個字元
    println!("{}", num_vec.capacity()); // 1 個元素: 印出 4. 一筆資料的 Vec 容量永遠從 4 開始
    num_vec.push('a'); // 多加一個
    num_vec.push('a'); // 多加一個
    num_vec.push('a'); // 多加一個
    println!("{}", num_vec.capacity()); // 4 個元素: 仍印出 4.
    num_vec.push('a'); // 多加一個
    println!("{}", num_vec.capacity()); // 印出 8. 我們有 5 個元素, 但容量從 4 加倍到 8 騰出了空間
}

印出:

0
4
4
8

所以這個向量再分配兩次:0 到 4,4 到 8。我們可以讓它更快:

fn main() {
    let mut num_vec = Vec::with_capacity(8); // 給它容量 8
    num_vec.push('a'); // 加一個字元
    println!("{}", num_vec.capacity()); // 印出 8
    num_vec.push('a'); // 再加一個
    println!("{}", num_vec.capacity()); // 印出 8
    num_vec.push('a'); // 再加一個
    println!("{}", num_vec.capacity()); // 印出 8.
    num_vec.push('a'); // 再加一個
    num_vec.push('a'); // 再加一個 // 現在我們有 5 個元素
    println!("{}", num_vec.capacity()); // 仍是 8
}

這個向量比較好再分配是 0 次。所以如果你認為你知道你需要多少元素,你可以使用 Vec::with_capacity() 來使它更快。

你記得你可以用 .into()&str 變成 String。你也可以用它把一個陣列變成 Vec。你必須告訴 .into() 你想要 Vec,但你可以不用選擇 Vec 的型別。如果你不想選擇,你可以寫 Vec<_>

fn main() {
    let my_vec: Vec<u8> = [1, 2, 3].into();
    let my_vec2: Vec<_> = [9, 0, 10].into(); // Vec<_> 表示 "幫我選 Vec 的型別"
                                             // Rust 會選 Vec<i32>
}