Rust 學習筆記:字串

2023年10月14日 · 中文


前言

本系列文書寫我個人初探 Rust 的學習筆記,章節劃分主要基於著名的 The BookThe Rust Programming Language,程式碼部分通常是個人閱讀消化後以方便說明的方式撰寫,完整學習建議直接參見該書。

The Rust Programming Language

該書也有中文翻譯版,不過個人閱讀以英文原版為主以鞏固對 terminology 的一致認識,我認為對未來閱讀以及查找資料會較為順暢。

不論語言該書都是相當優秀的學習資源,選擇適合你的語言開始學習 Rust 吧。

Appendix F: Translations of the Book

字串

字串在 Rust 中是相當複雜的,在 C 中字串只是一串 char,而在 Rust 中字串則有很多的表現形式,其中最常使用到的是 String&str

在 Rust 中字串與切片都是以 UTF-8 編碼的,通常情況我們並不需要擔心編碼的問題。

String

String 並不是 Rust 核心中內建的型別,他是在 std library 中提供的型別,因為它太常使用到因此包含在 prelude 中,會被 Rust 自動導入我們不需要手動導入它。

創建

new 方法建立空字串,或以 from 給予初始值。

let mut s = String::new();
let from = String::from("from");
println!("{from}");                           // from

to_string 方法可將有 Display trait 的型別轉換為 String。

let number_string = 5.to_string();
let literal_string = "literal".to_string();
println!("{number_string}");                  // 5
println!("{literal_string}");                 // literal

更新

建立可變字串,push 新增字元進字串,或以 push_str 新增字串切片,

let mut s = String::from("Hello");
s.push(' ');
s.push_str("World.");
println!("{s}");                              // Hello World.

使用 + 運算子串接字串,s2 必須使用參考,s1 的所有權會被轉移到 s3

let s1 = String::from("Hello ");
let s2 = String::from("world");
let s3 = s1 + &s2;

println!("{s3}");                             // Hello world

使用 format macro 更方便的串接字串格式,用法類似 printformat 使用的都是參考因此不會產生所有權的移轉。

let s1 = String::from("Hello ");
let s2 = String::from("new ");
let s3 = String::from("world");

let s = format!("{s1}{s2}{s3}");

println!("{s}");                              // Hello new world

// it's ok to use s1 s2 s3
println!("{s1}");
println!("{s2}");
println!("{s3}");

索引

Rust 不允許使用數字索引字串,因為 UTF-8 編碼每個字元的 byte 長度是不同的,會造成難以預期的結果,因此 Rust 不允許這樣的用法。

我們可以使用 chars 方法取回一個 iterator 來遍歷每個 char,這在英文不會有問題。

let s1 = String::from("Hello");
    
for char in s1.chars() {
    println!("{char}");
}

// output:
// H
// E
// L
// L
// O

但如過要使用其他語言可能會產生非預期的結果,如書上提供的印度語範例。

let s1 = String::from("नमस्ते");
    
for char in s1.chars() {
    print("{char} ");
}
// output: न म स ् त े 

這會印出六個數值,這是因為 chars 取得的是 scalar values,它不一定就是一般人認知的字母。

用另一種形式取得 grapheme clusters 會更接近一般人所認知的字母,這會需要引入 unicode_segmentation crate。

use unicode_segmentation::UnicodeSegmentation;

fn main() {
    let s1 = String::from("नमस्ते");
    
    for i in s1.graphemes(true) {
        print!("{i} ");
    }
}
// output: न म स् ते 

在使用切片時也會遇到類似的狀況,這邊我們必須給定 0..3 來取得

let s1 = String::from("नमस्ते");
println!("{}", &s1[0..3]);      // न

總之在操作字串尤其非英文的語言時一定要留意這類情形。