本文 AI 產出,尚未審核

Rust – 集合型別:字串(Strings)操作

簡介

在大多數程式語言中,字串是最常見的資料型別之一,幾乎所有的 I/O、日誌、網路協議、使用者介面都離不開它。Rust 以安全、效能為核心設計,對字串的處理也不例外。掌握 Rust 的字串操作,不僅能避免常見的記憶體安全問題,還能寫出高效、易讀的程式碼。

本篇文章針對 初學者到中級開發者,從基本概念到實務技巧,系統性說明 Rust 中的字串型別(String&str)以及常見的操作手法。透過實作範例,你將能快速上手,並在日常開發中避免常見陷阱。


核心概念

1. String vs. &str

型別 所屬位置 可變性 是否擁有所有權
String Heap(堆) 可變 (mut) 擁有
&str Stack(棧)或常量區 不可變 借用 (borrow)
  • String:在執行時動態分配記憶體,可使用 pushpush_str 等方法修改內容。
  • &str:字面值(如 "Hello")或從 String 中切割出的子字串,不可變,只是一個借用的切片。

Tip:在函式簽名中,若只需要讀取字串,盡量使用 &str,讓呼叫端自行決定是否傳入 String 或字面值。

2. 建立與轉換

// 建立空的 String
let mut s1 = String::new();

// 從字面值建立 String
let s2 = String::from("Rust");

// 使用 to_string()(實作了 ToString trait)
let s3 = "字串".to_string();

// &str 與 String 之間的轉換
let slice: &str = &s2;          // &String -> &str(自動解引用)
let owned: String = slice.to_owned(); // &str -> String

3. 常用方法概覽

方法 功能 範例
push(char) 在字串尾端加入單一字符 s.push('!');
push_str(&str) 在字串尾端加入字串切片 s.push_str(" world");
len() 回傳位元組長度(字元數) s.len()
chars() 產生 Unicode 字元的 iterator for c in s.chars() {}
bytes() 產生位元組的 iterator for b in s.bytes() {}
replace(old, new) 替換所有出現的子字串 s.replace("Rust", "Rustacean")
split(separator) 依分隔符切割 s.split(',')
trim() 去除前後空白 s.trim()
as_bytes() 取得位元組切片 (&[u8]) let bytes = s.as_bytes();

注意len() 回傳的是 位元組數,對於多位元組的 Unicode 字元(如中文)會比實際字元數大。

4. 程式碼範例

4.1 基本字串拼接

fn main() {
    // 使用 push 和 push_str
    let mut greeting = String::from("Hello");
    greeting.push(',');               // 加入單一字符
    greeting.push_str(" Rust!");      // 加入字串切片
    println!("{}", greeting); // => Hello, Rust!
}

4.2 文字切割與迭代

fn main() {
    let sentence = "Rust 是安全、快速且並發的程式語言";
    // 以空白切割
    for word in sentence.split(' ') {
        println!("單字: {}", word);
    }

    // 逐字元遍歷(Unicode 正確處理)
    for (i, ch) in sentence.chars().enumerate() {
        println!("第 {} 個字元: {}", i + 1, ch);
    }
}

4.3 替換與移除空白

fn main() {
    let raw = "   Rust   程式語言   ";
    // 先去除前後空白,再把多個空白合併成單一空格
    let cleaned = raw.trim().replace("   ", " ");
    println!("結果: \"{}\"", cleaned); // => "Rust 程式語言"
}

4.4 從 &str 生成 String 並取得位元組

fn main() {
    let slice: &str = "🦀 Crustacean";
    // 產生擁有所有權的 String
    let owned: String = slice.to_owned();

    // 取得 UTF-8 位元組
    let bytes = owned.as_bytes();
    println!("位元組長度: {}", bytes.len()); // 會大於字元數
}

4.5 使用 format! 產生新字串

fn main() {
    let name = "Alice";
    let age = 30;
    // format! 類似於 Python 的 f-string
    let intro = format!("我是 {}, 年齡 {} 歲。", name, age);
    println!("{}", intro);
}

常見陷阱與最佳實踐

陷阱 說明 解法 / 最佳實踐
位元組長度 ≠ 字元數 len() 會回傳 UTF‑8 位元組數,中文或 Emoji 會佔多個位元組。 若需要字元數,使用 s.chars().count()
切片邊界錯誤 直接以位元組索引切割 String(如 &s[0..1])在多位元組字元上會 panic。 使用 char_indices()unicode-segmentation crate 取得正確邊界。
不必要的 clone 直接 let s2 = s1.clone(); 會複製整個字串,耗費記憶體。 若只需要讀取,傳遞 &str;若需要所有權,使用 to_owned()into()
忘記 mut 嘗試在不可變 String 上呼叫 push 會編譯錯誤。 宣告變數時加上 mut,或使用 let mut s = String::from(...);
UTF‑8 編碼假設 手動操作位元組(如 s.as_bytes()[i])可能破壞字元完整性。 盡量使用 chars()bytes() 等安全迭代器,或使用 String::from_utf8 重新驗證。

最佳實踐

  1. &str 為函式介面:除非必須改變字串,否則使用 &str 讓呼叫者自行決定是否傳入 String、字面值或切片。
  2. 使用 format! 取代 + 連接format! 只會在需要時分配一次記憶體,效能較好且可讀性佳。
  3. 避免不必要的 clone:使用 Cow<'a, str>(Copy‑On‑Write)在需要時才取得所有權。
  4. 利用標準庫的 iteratorchars()split_whitespace()lines() 等提供高階抽象,讓程式更簡潔且安全。

實際應用場景

場景 需求 相關字串操作
日誌 (Logging) 組合時間戳、等級、訊息 format!("[{}][{}] {}", timestamp, level, msg)
命令列參數解析 取得子指令與參數 args.iter().skip(1).collect::<Vec<_>>().join(" ")
Web API 請求/回應 序列化/反序列化 JSON serde_json::to_string(&struct)serde_json::from_str::<T>(&s)
文字搜尋與取代 替換敏感詞、過濾 HTML 標籤 s.replace("<script>", "").replace("</script>", "")
多語系 UI 依語系載入對應字串檔案 let msg = translations.get(key).unwrap_or(&default).to_string();

案例:假設你在開發一個 CLI 工具,需要把使用者輸入的檔案路徑與選項組合成一條指令。利用 PathBufStringpush_strformat!,即可安全且可讀地完成:

use std::path::PathBuf;

fn build_command(file: &PathBuf, flag: bool) -> String {
    let mut cmd = String::from("my_tool");
    cmd.push(' ');
    cmd.push_str(file.to_str().unwrap()); // 轉成 &str
    if flag {
        cmd.push_str(" --verbose");
    }
    cmd
}

總結

  • Rust 的字串型別分為 擁有所有權的 String借用的 &str,了解兩者差異是安全操作的基礎。
  • 常用的字串方法(pushpush_strreplacesplittrim 等)讓我們能在不觸碰底層記憶體的情況下完成大多數需求。
  • 避免位元組與字元混用避免不必要的 clone,並盡量以 &str 作為函式介面,可提升效能與可讀性。
  • 在實務開發(日誌、CLI、Web API、國際化等)中,善用 format!、iterator 以及 serde 等生態系工具,能讓字串處理既簡潔又可靠。

掌握上述概念與技巧後,你就能在 Rust 專案中自信地處理各種文字資料,寫出 安全、效能佳且易於維護 的程式碼。祝你在 Rust 的旅程中玩得開心,寫出更好的程式!