Rust字符串類型全解析(最新推薦)
字符串是每種編程語(yǔ)言都繞不開的類型,
不過(guò),在Rust中,你會(huì)看到遠(yuǎn)比其他語(yǔ)言更加豐富多樣的字符串類型。
如下圖:

為什么Rust中需要這么多種表示字符串的類型呢?
初學(xué)Rust時(shí),可能無(wú)法理解為什么要這樣設(shè)計(jì)?為什么要給使用字符串帶來(lái)這么多不必要的復(fù)雜性?
其實(shí),Rust中對(duì)于字符串的設(shè)計(jì),優(yōu)先考慮的是安全,高效和靈活,
所以在易用性方面,感覺沒有其他語(yǔ)言(比如python,golang)那么易于理解和掌握。
本文嘗試解釋Rust中的所有不同的字符串類型,以及它們各自的特點(diǎn)。
希望能讓大家更好的理解Rust為了安全和發(fā)揮最大性能的同時(shí),是如何處理字符串的。
1. 機(jī)器中的字符串
我們代碼中的字符串或者數(shù)字,存儲(chǔ)在機(jī)器中,都是二進(jìn)制,也就是0和1組成的序列。

程序?qū)⒍M(jìn)制數(shù)據(jù)轉(zhuǎn)換為人類可讀的字符串 需要兩個(gè)關(guān)鍵信息:
- 字符編碼
- 字符串長(zhǎng)度
常見的編碼有ASCII,UTF-8等等,編碼就是二進(jìn)制序列對(duì)應(yīng)的字符,
比如,ASCII是8位二進(jìn)制對(duì)應(yīng)一個(gè)字符,所以它最多只能表示256種不同的字符。
而UTF-8可以使用8位~32位二進(jìn)制來(lái)表示一個(gè)字符,這意味著它可以編碼超過(guò)一百萬(wàn)個(gè)字符,
包括世界上的每種語(yǔ)言和各種表情符號(hào)等復(fù)雜字符。
通過(guò)字符編碼,我們可以將二進(jìn)制和字符互相轉(zhuǎn)換,
再通過(guò)字符串長(zhǎng)度信息,我們將內(nèi)存中的二進(jìn)制轉(zhuǎn)換為字符串時(shí),就能知道何時(shí)停止。
Rust中的字符串,統(tǒng)一采用UTF-8編碼,下面一一介紹各種字符串類型及其使用場(chǎng)景。
2. String 和 &str
String和&str是Rust中使用最多的兩種字符串類型,也是在使用中容易混淆的兩種類型。
String是分配在堆上的,可增長(zhǎng)的UTF-8字符串,
它擁有底層的數(shù)據(jù),并且在超出其定義的范圍被自動(dòng)清理釋放。
let my_string = String::from("databook");
println!(
"pointer: {:p}, length: {}, capacity: {}",
&my_string,
my_string.len(),
my_string.capacity()
);
對(duì)于一個(gè)String,主要部分有3個(gè):
Pointer:指向堆內(nèi)存中字符串的起始位置Length:有效字符串的長(zhǎng)度Capacity:字符串my_string總共占用的空間
注意這里Length和Capacity的區(qū)別,Length是my_string中有效字符的長(zhǎng)度,也就是字符串實(shí)際的長(zhǎng)度;
Capacity表示系統(tǒng)為my_string分配的內(nèi)存空間,一般來(lái)說(shuō),Capacity >= Length。
通常不需要直接處理Capacity,但它的存在對(duì)于編寫高效且資源敏感的Rust代碼時(shí)很重要。
特別是,當(dāng)你知道即將向String添加大量?jī)?nèi)容時(shí),可能會(huì)事先手動(dòng)保留足夠的Capacity以避免多次內(nèi)存重新分配。
&str則是一個(gè)字符串的切片,它表示一個(gè)連續(xù)的字符序列,
它是一個(gè)借用類型,并不擁有字符串?dāng)?shù)據(jù),只包含指向切片開頭的指針和切片長(zhǎng)度。
let my_str: &str = "databook";
println!("pointer: {:p}, length: {}", &my_str, my_str.len());
注意,&str沒有Capacity方法,因?yàn)樗皇且粋€(gè)借用,內(nèi)容不可能增加。
最后,對(duì)于String和&str,使用時(shí)建議:
- 在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建或修改字符串?dāng)?shù)據(jù)時(shí),請(qǐng)使用
String - 讀取或分析字符串?dāng)?shù)據(jù)而不對(duì)其進(jìn)行更改時(shí),請(qǐng)使用
&str
3. Vec[u8] 和 &[u8]
這兩種形式是將字符串表示位字節(jié)的形式,其中Vec[u8]是字節(jié)向量,&[u8]是字節(jié)切片。
它們只是將字符串中的各個(gè)字符轉(zhuǎn)換成字節(jié)形式。
as_bytes方法可將&str轉(zhuǎn)換為&[u8];
into_bytes方法可將String轉(zhuǎn)換為Vec<u8>。
let my_str: &str = "databook";
let my_string = String::from("databook");
let s: &[u8] = my_str.as_bytes();
let ss: Vec<u8> = my_string.into_bytes();
println!("s: {:?}", s);
println!("ss: {:?}", ss);
/* 運(yùn)行結(jié)果
s: [100, 97, 116, 97, 98, 111, 111, 107]
ss: [100, 97, 116, 97, 98, 111, 111, 107]
*/
在UTF-8編碼中,每個(gè)英文字母對(duì)應(yīng)1個(gè)字節(jié),而一個(gè)中文漢字對(duì)應(yīng)3個(gè)字節(jié)。
let my_str: &str = "中文";
let my_string = String::from("中文");
let s: &[u8] = my_str.as_bytes();
let ss: Vec<u8> = my_string.into_bytes();
println!("s: {:?}", s);
println!("ss: {:?}", ss);
/* 運(yùn)行結(jié)果
s: [228, 184, 173, 230, 150, 135]
ss: [228, 184, 173, 230, 150, 135]
*/
Vec[u8]和&[u8]以字節(jié)的形式存儲(chǔ)字符串,不用關(guān)心字符串的具體編碼,
這在網(wǎng)絡(luò)中傳輸二進(jìn)制文件或者數(shù)據(jù)包時(shí)非常有用,可以有效每次傳輸多少個(gè)字節(jié)。
4. str 系列
str類型本身是不能直接使用的,因?yàn)樗拇笮≡诰幾g期無(wú)法確定,不符合Rust的安全規(guī)則。
但是,它可以與其他具有特殊用途的指針類型一起使用。
4.1. Box<str>
如果需要一個(gè)字符串切片的所有權(quán)(&str是借用的,沒有所有權(quán)),那么可以使用Box智能指針。
當(dāng)你想要凍結(jié)字符串以防止進(jìn)一步修改或通過(guò)刪除額外容量來(lái)節(jié)省內(nèi)存時(shí),它非常有用。
比如,下面的代碼,我們將一個(gè)String轉(zhuǎn)換為Box<str>,
這樣,可以確保它不會(huì)在其他地方被修改,也可以刪除它,因?yàn)?code>Box<str>擁有字符串的所有權(quán)。
let my_string = String::from("databook");
let my_box_str = my_string.into_boxed_str();
println!("{}", my_box_str);
// 這一步會(huì)報(bào)錯(cuò),因?yàn)樗袡?quán)已經(jīng)轉(zhuǎn)移
// 這是 Box<str> 和 &str 的區(qū)別
// println!("{}", my_string);4.2. Rc<str>
當(dāng)你想要在多個(gè)地方共享一個(gè)不可變的字符串的所有權(quán),但是又不克隆實(shí)際的字符串?dāng)?shù)據(jù)時(shí),
可以嘗試使用Rc<str>智能指針。
比如,我們有一個(gè)非常大的文本,想在多個(gè)地方使用,又不想復(fù)制多份占用內(nèi)存,可以用Rc<str>。
let my_str: &str = "very long text ....";
let rc_str1: Rc<str> = Rc::from(my_str);
let rc_str2 = Rc::clone(&rc_str1);
let rc_str3 = Rc::clone(&rc_str1);
println!("rc_str1: {}", rc_str1);
println!("rc_str2: {}", rc_str2);
println!("rc_str3: {}", rc_str3);
/* 運(yùn)行結(jié)果
rc_str1: very long text ....
rc_str2: very long text ....
rc_str3: very long text ....
*/這樣,在不實(shí)際克隆字符串?dāng)?shù)據(jù)的情況下,讓多個(gè)變量擁有其所有權(quán)。
4.3. Arc<str>
Arc<str>與Rc<str>的功能類似,主要的區(qū)別在于Arc<str>是線程安全的。
如果在多線程環(huán)境下,請(qǐng)使用Arc<str>。
let my_str: &str = "very long text ....";
let arc_str: Arc<str> = Arc::from(my_str);
let mut threads = vec![];
let mut cnt = 0;
while cnt < 5 {
let s = Arc::clone(&arc_str);
let t = thread::spawn(move || {
println!("thread-{}: {}", cnt, s);
});
threads.push(t);
cnt += 1;
}
for t in threads {
t.join().unwrap();
}
/* 運(yùn)行結(jié)果
thread-0: very long text ....
thread-3: very long text ....
thread-2: very long text ....
thread-1: very long text ....
thread-4: very long text ....
*/上面的代碼中,在5個(gè)線程中共享了字符串?dāng)?shù)據(jù)。
上面運(yùn)行結(jié)果中,線程順序是不固定的,多執(zhí)行幾遍會(huì)有不一樣的順序。
4.4. Cow<str>
Cow是Copy-on-Write(寫入時(shí)復(fù)制)的縮寫,
當(dāng)你需要實(shí)現(xiàn)一個(gè)功能,根據(jù)字符串的內(nèi)容來(lái)決定是否需要修改它,使用Cow就很合適。
比如,過(guò)濾敏感詞匯時(shí),我們把敏感詞匯替換成xx。
fn filter_words(input: &str) -> Cow<str> {
if input.contains("sb") {
let output = input.replace("sb", "xx");
return Cow::Owned(output);
}
Cow::Borrowed(input)
}當(dāng)輸入字符串input中含有敏感詞sb時(shí),會(huì)重新分配內(nèi)存,生成新字符串;
否則直接使用原字符串,提高內(nèi)存效率。
5. CStr 和 CString
CStr和CString是與C語(yǔ)言交互時(shí)用于處理字符串的兩種類型。
CStr用于在Rust中安全地訪問(wèn)由C語(yǔ)言分配的字符串;
而CString用于在Rust中創(chuàng)建和管理可以安全傳遞給C語(yǔ)言函數(shù)的字符串。
C風(fēng)格的字符串與Rust中的字符串實(shí)現(xiàn)方式不一樣,
比如,C語(yǔ)言中的字符串都是以null字符\0結(jié)尾的字節(jié)數(shù)組,這點(diǎn)就與Rust很不一樣。
所以Rust單獨(dú)封裝了這兩種類型(CStr和CString),可以安全的與C語(yǔ)言進(jìn)行字符串交互,從而實(shí)現(xiàn)與現(xiàn)有的C語(yǔ)言庫(kù)和API無(wú)縫集成。
6. OsStr 和 OsString
OsStr 和 OsString 是用于處理與操作系統(tǒng)兼容的字符串類型。
主要用于需要與操作系統(tǒng)API進(jìn)行交互的場(chǎng)景,這些API一般特定于平臺(tái)的字符串編碼(比如Windows上的UTF-16,以及大多數(shù)Unix-like系統(tǒng)上的UTF-8)。
OsStr 和OsString 也相當(dāng)于str和String的關(guān)系,所以OsStr 一般不直接在代碼中使用,
使用比較多的是&OsStr和OsString。
這兩個(gè)類型一般用于讀取/寫入操作系統(tǒng)環(huán)境變量或者與系統(tǒng)API交互時(shí),幫助我們確保字符串以正確的格式傳遞。
7. Path 和 PathBuf
這兩個(gè)類型看名字似乎和字符串關(guān)系不大,實(shí)際上它們是專門用來(lái)處理文件路徑字符串的。
在不同的文件系統(tǒng)中,對(duì)于文件路徑的格式,路徑中允許使用的字符都不一樣,比如,windows系統(tǒng)中文件路徑甚至不區(qū)分大小寫。
使用Path 和 PathBuf,我們編碼時(shí)就不用分散精力去關(guān)心具體使用的是哪種文件系統(tǒng)。
Path和PathBuf的主要區(qū)別在于可變性和所有權(quán),
如果需要頻繁讀取和查詢路徑信息而不修改它,Path是一個(gè)好選擇;
如果需要?jiǎng)討B(tài)構(gòu)建或修改路徑內(nèi)容,PathBuf則更加合適。
8. 總結(jié)
總之,Rust中字符串類型之所以多,是因?yàn)楦鶕?jù)不同的用途對(duì)字符串類型做了分類。
這也是為了處理不同的應(yīng)用場(chǎng)景時(shí)讓程序發(fā)揮最大的性能,畢竟,安全和高性能一直是Rust最大的賣點(diǎn)。
到此這篇關(guān)于Rust字符串類型全解析(最新推薦)的文章就介紹到這了,更多相關(guān)Rust字符串類型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談Rust?+=?運(yùn)算符與?MIR?應(yīng)用
這篇文章主要介紹了Rust?+=?運(yùn)算符與?MIR?應(yīng)用,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-01-01
如何使用bindgen將C語(yǔ)言頭文件轉(zhuǎn)換為Rust接口代碼
這篇文章主要介紹了使用bindgen將C語(yǔ)言頭文件轉(zhuǎn)換為Rust接口代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-01-01
Rust 中單線程 Web 服務(wù)器的實(shí)現(xiàn)
本文用Rust構(gòu)建單線程Web服務(wù)器,通過(guò)HTTP/TCP處理請(qǐng)求,返回hello.html或404.html,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-06-06

