上一篇我們學到 Rust 有個東西叫陣列,它的長度固定,不能隨意改變,聽起來非常難用。
那有沒有像陣列一樣可以儲存一串資料,但長度可以改變的東西呢?有的!它就是我們今天的主角——向量

上一篇:04_primitive_types
本系列主頁面:Rust繁中簡學!
繁體中文版 Rustlings:https://github.com/TimLai666/rustlings-zh-TW
安裝方法:00_intro

05_vecs(向量)

向量

向量是 Rust 中使用最廣泛的資料結構之一。在其他程式語言中,它們可能被簡單地稱為數組,但由於 Rust 操作的是較低層次的數據,數組在 Rust 中是存儲在棧上的(stack,意味著它不能增長或縮小,大小需要在編譯時知道),而向量是存儲在堆上的(heap,這些限制不適用)。

向量在書中屬於稍後的章節,但我們認為它們足夠有用,值得提前介紹。我們將在後面討論另一個有用的資料結構:雜湊表。

進一步了解

這次終於寫了一些有用的東西。

練習一(vecs1.rs

// vecs1.rs
//
// 您的任務是創建一個 `Vec`,其中包含與陣列 `a` 完全相同的元素。
//
// 讓我編譯並通過測試!
//
// 執行 `rustlings hint vecs1` 或使用 `hint` watch 子命令來獲取提示。

// I AM NOT DONE

fn array_and_vec() -> ([i32; 4], Vec<i32>) {
    let a = [10, 20, 30, 40]; // 一個普通的陣列
    let v = // TODO: 使用向量的巨集在這裡宣告您的向量

    (a, v)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_array_and_vec_similarity() {
        let (a, v) = array_and_vec();
        assert_eq!(a, v[..]);
    }
}
! 編譯 exercises/05_vecs/vecs1.rs 失敗!請再試一次。以下是輸出:
error: expected `;`, found `}`
  --> exercises/05_vecs/vecs1.rs:15:11
   |
15 |     (a, v)
   |           ^ help: add `;` here
16 | }
   | - unexpected token

error[E0425]: cannot find value `v` in this scope
  --> exercises/05_vecs/vecs1.rs:15:9
   |
15 |     (a, v)
   |         ^ help: a local variable with a similar name exists: `a`

error[E0308]: mismatched types
  --> exercises/05_vecs/vecs1.rs:11:23
   |
11 | fn array_and_vec() -> ([i32; 4], Vec<i32>) {
   |    -------------      ^^^^^^^^^^^^^^^^^^^^ expected `([i32; 4], Vec<i32>)`, found `()`
   |    |
   |    implicitly returns `()` as its body has no tail or `return` expression
   |
   = note:  expected tuple `([i32; 4], Vec<i32>)`
           found unit type `()`

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0308, E0425.
For more information about an error, try `rustc --explain E0308`.

這題要用向量巨集來宣告一個元素跟它給的陣列一樣的向量。

向量巨集的用法很簡單:

 let v = vec![1, 2, 3];
// vecs1.rs
//
// 您的任務是創建一個 `Vec`,其中包含與陣列 `a` 完全相同的元素。
//
// 讓我編譯並通過測試!
//
// 執行 `rustlings hint vecs1` 或使用 `hint` watch 子命令來獲取提示。

// I AM NOT DONE

fn array_and_vec() -> ([i32; 4], Vec<i32>) {
    let a = [10, 20, 30, 40]; // 一個普通的陣列
    let v = vec![10, 20, 30, 40]; // TODO: 使用向量的巨集在這裡宣告您的向量

    (a, v)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_array_and_vec_similarity() {
        let (a, v) = array_and_vec();
        assert_eq!(a, v[..]);
    }
}
✓ 成功測試 exercises/05_vecs/vecs1.rs!

🎉 🎉 代碼正在編譯,並且測試通過! 🎉 🎉

您可以繼續進行此練習,
或通過刪除 `I AM NOT DONE` 註釋來進入下一個練習:

 7 |  // 執行 `rustlings hint vecs1` 或使用 `hint` watch 子命令來獲取提示。
 8 |  
 9 |  // I AM NOT DONE
10 |  
11 |  fn array_and_vec() -> ([i32; 4], Vec<i32>) {

練習二(vecs2.rs

// vecs2.rs
//
// 給定了一個包含偶數的 Vec。您的任務是完成循環,使 Vec 中的每個數字都乘以 2。
//
// 讓我通過測試!
//
// 執行 `rustlings hint vecs2` 或使用 `hint` watch 子命令來獲取提示。

// I AM NOT DONE

fn vec_loop(mut v: Vec<i32>) -> Vec<i32> {
    for element in v.iter_mut() {
        // TODO: 填寫這裡,使 Vec `v` 中的每個元素都乘以 2。
        ???
    }

    // 在這個點,`v` 應該等於 [4, 8, 12, 16, 20]。
    v
}

fn vec_map(v: &Vec<i32>) -> Vec<i32> {
    v.iter().map(|element| {
        // TODO: 做與上面相同的事情 - 但不修改 Vec,您可以直接返回新的數字!
        ???
    }).collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_vec_loop() {
        let v: Vec<i32> = (1..).filter(|x| x % 2 == 0).take(5).collect();
        let ans = vec_loop(v.clone());

        assert_eq!(ans, v.iter().map(|x| x * 2).collect::<Vec<i32>>());
    }

    #[test]
    fn test_vec_map() {
        let v: Vec<i32> = (1..).filter(|x| x % 2 == 0).take(5).collect();
        let ans = vec_map(&v);

        assert_eq!(ans, v.iter().map(|x| x * 2).collect::<Vec<i32>>());
    }
}
! 編譯 exercises/05_vecs/vecs2.rs 失敗!請再試一次。以下是輸出:
error: expected expression, found `?`
  --> exercises/05_vecs/vecs2.rs:14:9
   |
14 |         ???
   |         ^ expected expression

error: expected expression, found `?`
  --> exercises/05_vecs/vecs2.rs:24:9
   |
24 |         ???
   |         ^ expected expression

error: aborting due to 2 previous errors

這題給了我們一個向量,我們要把裡面的元素全部乘以 2,上面那一個要改變向量裡的元素,下面的則不用。

改變向量裡的元素稍微複雜一點,但寫法還是很簡潔。

我們還可以遍歷可變向量中的每個元素取得可變參考來改變每個元素。像是範例 8-8 就使用 for 迴圈來為每個元素加上 50

    let mut v = vec![100, 32, 57];
    for i in &mut v {
        *i += 50;
    }

範例 8-8:遍歷向量中的元素取得可變參考

要改變可變參考指向的數值,在使用 += 運算子之前,我們需要使用 * 解參考運算子來取得 i 的數值。我們會在第十五章的「追蹤指標的數值」段落來講解更多解參考運算子的細節。

題目已經幫我們寫好半個 for 迴圈了:

    for element in v.iter_mut() {
        // TODO: 填寫這裡,使 Vec `v` 中的每個元素都乘以 2。
        ???
    }

只要在 for 迴圈裡加上 *element *= 2;

fn vec_loop(mut v: Vec<i32>) -> Vec<i32> {
    for element in v.iter_mut() {
        // TODO: 填寫這裡,使 Vec `v` 中的每個元素都乘以 2。
        *element *= 2;
    }

    // 在這個點,`v` 應該等於 [4, 8, 12, 16, 20]。
    v
}

第二個因為不需要改變向量裡的元素,所以直接 element * 2 即可。

// vecs2.rs
//
// 給定了一個包含偶數的 Vec。您的任務是完成循環,使 Vec 中的每個數字都乘以 2。
//
// 讓我通過測試!
//
// 執行 `rustlings hint vecs2` 或使用 `hint` watch 子命令來獲取提示。

// I AM NOT DONE

fn vec_loop(mut v: Vec<i32>) -> Vec<i32> {
    for element in v.iter_mut() {
        // TODO: 填寫這裡,使 Vec `v` 中的每個元素都乘以 2。
        *element *= 2;
    }

    // 在這個點,`v` 應該等於 [4, 8, 12, 16, 20]。
    v
}

fn vec_map(v: &Vec<i32>) -> Vec<i32> {
    v.iter().map(|element| {
        // TODO: 做與上面相同的事情 - 但不修改 Vec,您可以直接返回新的數字!
        element * 2
    }).collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_vec_loop() {
        let v: Vec<i32> = (1..).filter(|x| x % 2 == 0).take(5).collect();
        let ans = vec_loop(v.clone());

        assert_eq!(ans, v.iter().map(|x| x * 2).collect::<Vec<i32>>());
    }

    #[test]
    fn test_vec_map() {
        let v: Vec<i32> = (1..).filter(|x| x % 2 == 0).take(5).collect();
        let ans = vec_map(&v);

        assert_eq!(ans, v.iter().map(|x| x * 2).collect::<Vec<i32>>());
    }
}
✓ 成功測試 exercises/05_vecs/vecs2.rs!

🎉 🎉 代碼正在編譯,並且測試通過! 🎉 🎉

您可以繼續進行此練習,
或通過刪除 `I AM NOT DONE` 註釋來進入下一個練習:

 7 |  // 執行 `rustlings hint vecs2` 或使用 `hint` watch 子命令來獲取提示。
 8 |  
 9 |  // I AM NOT DONE
10 |  
11 |  fn vec_loop(mut v: Vec<i32>) -> Vec<i32> {

總結

雖然這個單元只有兩題,但程式碼越來越難懂了,我自己也不太能完全理解。

從這次的練習中,我們得知:

  • 向量可以用 vec![元素] 來宣告。
  • 用 for 迴圈來讀取或改變向量裡的元素。

其餘的我其實也不太會,之後如果弄懂了再來補充吧。希望這個系列不會就這樣夭折😅

p.s. 還是 Python 的串列好用😝

Similar Posts

One Comment

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *