Rust 語言:基本語法與變數 ─ 陣列(Arrays)與元組(Tuples)
簡介
在程式設計中,陣列與元組是最常見的集合型別。它們讓我們可以一次儲存多個值,並以有序的方式存取。對於 Rust 這樣強調安全與效能的系統程式語言,正確掌握這兩種資料結構不僅能提升程式的可讀性,更能避免許多潛在的記憶體錯誤。
本單元將帶你從最基本的宣告語法出發,逐步了解陣列與元組的差異、使用情境以及在實務開發中常見的最佳實踐。即使你是剛接觸 Rust 的新手,也能在閱讀完本文後,自信地在專案中運用這兩種資料結構。
核心概念
1. 陣列(Array)
陣列是一組長度固定、元素型別相同的值。其長度在編譯期就必須確定,這讓編譯器能夠在記憶體配置上做最佳化。
1.1 宣告與初始化
// 宣告一個長度為 5、元素型別為 i32 的陣列,全部初始化為 0
let numbers: [i32; 5] = [0; 5];
// 直接列舉初始值
let primes = [2, 3, 5, 7, 11];
[0; 5] 代表「5 個 0」的簡寫。
1.2 取值與切片
let first = primes[0]; // 取得第一個元素,值為 2
let last = primes[primes.len() - 1]; // 取得最後一個元素
// 取得子陣列(切片),類型是 &[i32]
let middle = &primes[1..4]; // 包含索引 1、2、3 的三個元素
切片 (&[T]) 只是一個「指向陣列一段連續記憶體」的引用,不會產生所有權的轉移。
1.3 陣列與迭代
for (i, value) in primes.iter().enumerate() {
println!("第 {} 個質數是 {}", i + 1, value);
}
iter() 產生不可變的迭代器,enumerate() 同時提供索引與值,讓印出陣列內容變得簡潔。
2. 元組(Tuple)
元組是一組長度固定、型別可以不同的值。與陣列不同,元組的每個欄位可以是任意型別,甚至是另一個元組或陣列。
2.1 宣告與解構
// 宣告一個包含三個不同型別的元組
let person: (&str, u8, f64) = ("Alice", 30, 165.5);
// 直接解構取值
let (name, age, height) = person;
println!("姓名: {}, 年齡: {}, 身高: {}cm", name, age, height);
解構 (let (a, b, c) = tuple;) 讓我們一次取得所有欄位的值,語法上非常直觀。
2.2 透過索引存取
let second = person.1; // 取得年齡,等同於 age
元組的欄位編號是從 0 開始的整數常數,編譯期即已確定。
2.3 元組作為函式回傳值
fn min_max(arr: &[i32]) -> (i32, i32) {
let mut min = arr[0];
let mut max = arr[0];
for &v in arr.iter().skip(1) {
if v < min { min = v; }
if v > max { max = v; }
}
(min, max) // 回傳一個 (最小值, 最大值) 的元組
}
let data = [3, 7, 2, 9, 4];
let (min, max) = min_max(&data);
println!("最小值 {},最大值 {}", min, max);
使用元組回傳多個相關結果,可避免額外建立結構體或使用全域變數。
3. 陣列 vs 元組:何時使用?
| 特性 | 陣列 | 元組 |
|---|---|---|
| 長度 | 編譯期固定,所有元素相同長度 | 編譯期固定,長度由欄位數決定 |
| 元素型別 | 必須相同 | 可以不同 |
| 常見用途 | 同類型資料的批次處理、數值運算 | 結構化資料、函式多返回值、輕量級的「record」 |
| 存取方式 | arr[i]、切片 &arr[start..end] |
tuple.0、解構 let (a,b) = tuple; |
簡單來說,當你需要同型別、固定長度的集合時,使用陣列;當你想要把幾個不同型別的值打包在一起,且不需要大量的迭代或切片功能時,使用元組。
常見陷阱與最佳實踐
1. 越界存取
let a = [1, 2, 3];
let x = a[3]; // ❌ 編譯通過,但執行時 panic!
Rust 會在執行時檢查陣列索引,若越界會觸發 panic。建議使用 get 方法:
if let Some(v) = a.get(3) {
println!("第 4 個元素是 {}", v);
} else {
println!("索引超出範圍");
}
2. 元組欄位過多
元組的欄位數量若超過 12 個,編譯器會開始產生較大的元組實作,導致編譯速度下降且可讀性變差。此時改用結構體會更合適。
3. 不必要的拷貝
陣列的元素若是大型結構體,直接 let b = a; 會觸發完整拷貝(因為陣列實作 Copy 只有在元素也實作 Copy 時才會)。使用引用或切片可以避免:
let big = [String::from("a"); 3];
let slice = &big; // 只借用,不拷貝
4. 切片與元組的混用
切片只能對陣列或 Vec<T> 使用,不能直接對元組切片。如果需要「類似切片」的功能,請考慮把元組轉成陣列或 Vec,或直接使用結構體。
5. 常量陣列的最佳化
若陣列內容在編譯期已知且不會改變,使用 const 或 static 可以讓編譯器將其放入只讀段,減少執行時的記憶體配置:
static DAYS: [&str; 7] = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
實際應用場景
設定檔的固定選項
使用static陣列儲存語系代碼、錯誤代碼等不變集合,搭配切片查找可以保證執行效能。座標與向量
3D 圖形程式常用let position: [f32; 3] = [x, y, z];表示座標;若需要同時傳遞「位置」與「顏色」等不同型別資訊,可用( [f32;3], [u8;4] )的元組。函式多返回值
計算統計資訊(最小值、最大值、平均值)時,fn stats(data: &[i32]) -> (i32, i32, f64)用元組一次回傳全部結果,呼叫端只需解構即可。狀態機
使用元組搭配模式匹配 (match) 可以簡潔描述多狀態的資料,例如(State::Idle, elapsed: u64)。資料序列化
在與 C 語言或硬體介面交互時,陣列的固定長度與連續記憶體布局非常重要,Rust 的[T; N]正好符合這類需求。
總結
陣列與元組是 Rust 中最基礎、也是最實用的集合型別。陣列適合處理同型別、固定長度的批次資料,提供快速索引與切片功能;元組則是把不同型別的值打包在一起的利器,常用於函式的多返回值或輕量級的資料結構。
掌握它們的宣告、存取、迭代方式,以及避免常見的越界與拷貝問題,能讓你在寫 Rust 程式時更得心應手。未來在面對更複雜的資料結構(如 Vec<T>、HashMap<K,V>)時,這些基礎概念也會成為你快速上手的關鍵。祝你在 Rust 的學習路上,玩得開心、寫得安全!