本文 AI 產出,尚未審核

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. 常量陣列的最佳化

若陣列內容在編譯期已知且不會改變,使用 conststatic 可以讓編譯器將其放入只讀段,減少執行時的記憶體配置:

static DAYS: [&str; 7] = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];

實際應用場景

  1. 設定檔的固定選項
    使用 static 陣列儲存語系代碼、錯誤代碼等不變集合,搭配切片查找可以保證執行效能。

  2. 座標與向量
    3D 圖形程式常用 let position: [f32; 3] = [x, y, z]; 表示座標;若需要同時傳遞「位置」與「顏色」等不同型別資訊,可用 ( [f32;3], [u8;4] ) 的元組。

  3. 函式多返回值
    計算統計資訊(最小值、最大值、平均值)時,fn stats(data: &[i32]) -> (i32, i32, f64) 用元組一次回傳全部結果,呼叫端只需解構即可。

  4. 狀態機
    使用元組搭配模式匹配 (match) 可以簡潔描述多狀態的資料,例如 (State::Idle, elapsed: u64)

  5. 資料序列化
    在與 C 語言或硬體介面交互時,陣列的固定長度與連續記憶體布局非常重要,Rust 的 [T; N] 正好符合這類需求。


總結

陣列與元組是 Rust 中最基礎、也是最實用的集合型別。陣列適合處理同型別、固定長度的批次資料,提供快速索引與切片功能;元組則是把不同型別的值打包在一起的利器,常用於函式的多返回值或輕量級的資料結構。

掌握它們的宣告、存取、迭代方式,以及避免常見的越界與拷貝問題,能讓你在寫 Rust 程式時更得心應手。未來在面對更複雜的資料結構(如 Vec<T>HashMap<K,V>)時,這些基礎概念也會成為你快速上手的關鍵。祝你在 Rust 的學習路上,玩得開心、寫得安全