跳轉至

建立 2026-09-19 更新 2026-09-19

字串與集合

實際的程式大量處理文字與成批的資料。本頁介紹三個最常用的堆積型別:字串 String、動態陣列 Vec<T>、鍵值對照表 HashMap<K, V>。它們都擁有自己的資料,會在離開作用域時自動釋放。

字串:String 與 &str

Rust 有兩種主要的字串型別:

型別 說明 使用時機
String 擁有資料、可成長的字串,存在堆積上 需要建立、修改或儲存字串
&str 字串切片,唯讀參照 函式參數、字串字面值
fn main() {
    let mut s = String::from("Hello");
    s.push_str(", Rust");
    s.push('!');
    println!("{s}");

    let a = String::from("Hello");
    let b = String::from("World");
    let joined = format!("{a}, {b}"); // format! 不會取走 a、b 的所有權
    println!("{joined}");

    let borrowed: &str = &joined; // String 可以借用成 &str
    println!("{}", borrowed.len());
}

字串是 UTF-8,不能用整數索引

Rust 字串以 UTF-8 編碼,一個中文字佔 3 個位元組。len() 回傳的是位元組數,不是字元數,所以 Rust 不允許 s[0] 這種寫法。要處理字元,請用 chars()

fn main() {
    let s = "你好,Rust";
    println!("位元組數:{}", s.len());
    println!("字元數:{}", s.chars().count());

    for c in s.chars() {
        print!("[{c}]");
    }
    println!();
}

Vec:動態陣列

Vec<T> 是可以成長的陣列,元素型別必須相同:

fn main() {
    let mut v = vec![1, 2, 3];
    v.push(4);
    v.push(5);

    println!("{:?},長度 {}", v, v.len());

    // 用索引取值:越界會 panic
    println!("{}", v[0]);

    // 用 get 取值:回傳 Option,較安全
    match v.get(10) {
        Some(n) => println!("{n}"),
        None => println!("索引 10 不存在"),
    }

    // 走訪並修改
    for n in &mut v {
        *n *= 2;
    }
    println!("{:?}", v);

    // 移除最後一個元素
    let last = v.pop();
    println!("{:?} {:?}", last, v);
}

for n in &mut v 取得每個元素的可修改參照,需要用 *n 解參照才能改值。

走訪時不能修改 Vec 本身

在走訪 Vec 的同時呼叫 push 會違反借用規則(走訪持有參照,push 需要可修改參照),編譯器會拒絕。這正是借用規則在保護你,避免因為重新配置記憶體造成參照失效。

HashMap:鍵值對照表

HashMap 需要先引入,儲存的順序不固定:

use std::collections::HashMap;

fn main() {
    let mut scores: HashMap<String, i32> = HashMap::new();
    scores.insert(String::from("小明"), 90);
    scores.insert(String::from("小華"), 85);

    // 取值:回傳 Option
    if let Some(score) = scores.get("小明") {
        println!("小明:{score}");
    }

    // 覆寫
    scores.insert(String::from("小明"), 95);

    // 走訪(順序不固定)
    let mut names: Vec<_> = scores.keys().collect();
    names.sort();
    for name in names {
        println!("{name}:{}", scores[name]);
    }
}

entry:不存在才插入

統計次數是 HashMap 的經典用法,entry(...).or_insert(...) 能一次處理「有就更新、沒有就新增」:

use std::collections::HashMap;

fn main() {
    let text = "the quick brown fox jumps over the lazy dog the end";
    let mut counts: HashMap<&str, i32> = HashMap::new();

    for word in text.split_whitespace() {
        *counts.entry(word).or_insert(0) += 1;
    }

    let mut pairs: Vec<_> = counts.iter().collect();
    pairs.sort_by(|a, b| b.1.cmp(a.1).then(a.0.cmp(b.0)));
    for (word, count) in pairs.iter().take(3) {
        println!("{word}: {count}");
    }
}

or_insert 回傳該值的可修改參照,所以用 * 解參照後就能直接加一。

該選哪一個?

需求 選擇
固定長度、編譯時已知 陣列 [T; N]
一串會增減的同型別資料 Vec<T>
用鍵快速查值 HashMap<K, V>
建立、修改文字 String
只讀取文字(函式參數) &str

推薦影音

字串

簡述:微軟《Beginner's Series to Rust》第 29 集,介紹 String&str 的差別與常見操作。

常見集合

簡述:Let's Get Rusty 依照官方書籍第 8 章製作,依序講解 VecStringHashMap,對應本頁全部內容。

資料建模到此完成。接下來學習如何寫出可重用的程式碼:抽象與錯誤處理