本文 AI 產出,尚未審核
Rust – 集合型別:字串(Strings)操作
簡介
在大多數程式語言中,字串是最常見的資料型別之一,幾乎所有的 I/O、日誌、網路協議、使用者介面都離不開它。Rust 以安全、效能為核心設計,對字串的處理也不例外。掌握 Rust 的字串操作,不僅能避免常見的記憶體安全問題,還能寫出高效、易讀的程式碼。
本篇文章針對 初學者到中級開發者,從基本概念到實務技巧,系統性說明 Rust 中的字串型別(String、&str)以及常見的操作手法。透過實作範例,你將能快速上手,並在日常開發中避免常見陷阱。
核心概念
1. String vs. &str
| 型別 | 所屬位置 | 可變性 | 是否擁有所有權 |
|---|---|---|---|
String |
Heap(堆) | 可變 (mut) |
擁有 |
&str |
Stack(棧)或常量區 | 不可變 | 借用 (borrow) |
String:在執行時動態分配記憶體,可使用push、push_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 重新驗證。 |
最佳實踐
- 以
&str為函式介面:除非必須改變字串,否則使用&str讓呼叫者自行決定是否傳入String、字面值或切片。 - 使用
format!取代+連接:format!只會在需要時分配一次記憶體,效能較好且可讀性佳。 - 避免不必要的 clone:使用
Cow<'a, str>(Copy‑On‑Write)在需要時才取得所有權。 - 利用標準庫的 iterator:
chars()、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 工具,需要把使用者輸入的檔案路徑與選項組合成一條指令。利用
PathBuf與String的push_str、format!,即可安全且可讀地完成:
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,了解兩者差異是安全操作的基礎。 - 常用的字串方法(
push、push_str、replace、split、trim等)讓我們能在不觸碰底層記憶體的情況下完成大多數需求。 - 避免位元組與字元混用、避免不必要的 clone,並盡量以
&str作為函式介面,可提升效能與可讀性。 - 在實務開發(日誌、CLI、Web API、國際化等)中,善用
format!、iterator 以及serde等生態系工具,能讓字串處理既簡潔又可靠。
掌握上述概念與技巧後,你就能在 Rust 專案中自信地處理各種文字資料,寫出 安全、效能佳且易於維護 的程式碼。祝你在 Rust 的旅程中玩得開心,寫出更好的程式!