DEV Community

Daniel Lin
Daniel Lin

Posted on

String in Rust

之前看到一個有趣的圖,在 c 中 string 其實就是 char*,而在 rust 中 string 卻有很多種型別,對寫 Golang 的我來說光是為何要 to_string() 才能 append string 就滿頭問號。

slice, array, vector

講 string 前可以先理解 rust 中 slice, array, vector 的定義
array 為固定長度的序列
[T; N] T 是類型 N 是 長度

let s = [1; 5];
println!("{:?}", s);
// [1, 1, 1, 1, 1]
Enter fullscreen mode Exit fullscreen mode

vector 是可以動態增長的序列

let mut s = vec![1];
s.push(2);
assert_eq!(s, &[1, 2]);
assert_eq!(s.len(), 2);
assert_ne!(s.capacity(), 2);
Enter fullscreen mode Exit fullscreen mode

slice 則是對連續空間的 reference,可以是 vector / array 的一部分參考,可以不需要經過複製就可以對 slice 參照的 vector / array 進行操作

常見的 &str 和 String 的差別

&str

let s = "abc";
Enter fullscreen mode Exit fullscreen mode

在編譯時因為 "abc" 長度是固定的所以被認為是靜態的放 stack 上,所以引用時去參照這段位置,所以 s 為 str 這個類別的 reference &str,而 &str 其實是一個 slice 參照 u8 的 array,

String

let s = "abc".to_string();
Enter fullscreen mode Exit fullscreen mode

s 是 String 其實是一個 u8 的 vector,因為是可以動態增長,compiler 不會知道 s 會長多大,所以必須放在 heap 上,可以做下面的操作來改變 String 本身

let mut s = "abc".to_string();
s.push_str("def");
Enter fullscreen mode Exit fullscreen mode

那為何 rust 不把我定義的 "abc" 值當作 [u8] 就好了呢? 因為 rust 還同時要確保 &str 中的 [u8] 是合法的 UTF-8 字元,就像大部份語言 char 與 string 的區別,這種 pointer 包含了 type 還有其他規則的 pointer 常被稱為 fat pointer。

可以看出來其實 &str 與 String 類型的差別跟 compiler 將 data 放在 heap 還是 stack 有很大的關係,當你對 &str 使用 to_string 就產生的 memory allocation,所以不需要就不要使用 to_string 增加開銷。

其他意外發現的坑

let s = "🗻∈🌏";
let byte = s.as_bytes();

assert_eq!(s.len(), 11);
assert_eq!(s.chars().count(), 3);
assert_eq!(byte.len(), 11);
Enter fullscreen mode Exit fullscreen mode

s.len() 竟然是 11 讓我有點傻眼XD...

除此之外,如果是 precomposed characters 會有很神奇的現象,這讓我理解到數字其實背後也有很多學問,chars count 是數 Unicode Scalar Value,而 "é" 是兩個 unicode 組成,所以直接算就會是兩個字

use unicode_segmentation::UnicodeSegmentation; // 1.6.0
fn main() {
    let s = "é";
    println!("{}", s.chars().count()); // 2
    println!("{}", "é".graphemes(true).count()); // 1
}
Enter fullscreen mode Exit fullscreen mode

神奇的是如果是直接引用用 literal 兩次會得到不一樣的結果

println!("{}", "é".chars().count()); // 2
println!("{}", "é".chars().count()); // 1
Enter fullscreen mode Exit fullscreen mode

https://stackoverflow.com/questions/46290655/get-the-string-length-in-characters-in-rust
至於為何會不一樣又是另外一個故事了。

Discussion (0)