Rust字符串類型全解析(最新推薦)
字符串是每種編程語(yǔ)言都繞不開(kāi)的類型,
不過(guò),在Rust
中,你會(huì)看到遠(yuǎn)比其他語(yǔ)言更加豐富多樣的字符串類型。
如下圖:
為什么Rust
中需要這么多種表示字符串的類型呢?
初學(xué)Rust
時(shí),可能無(wú)法理解為什么要這樣設(shè)計(jì)?為什么要給使用字符串帶來(lái)這么多不必要的復(fù)雜性?
其實(shí),Rust
中對(duì)于字符串的設(shè)計(jì),優(yōu)先考慮的是安全,高效和靈活,
所以在易用性方面,感覺(jué)沒(méi)有其他語(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)度
常見(jiàn)的編碼有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ù),只包含指向切片開(kāi)頭的指針和切片長(zhǎng)度。
let my_str: &str = "databook"; println!("pointer: {:p}, length: {}", &my_str, my_str.len());
注意,&str
沒(méi)有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
是借用的,沒(méi)有所有權(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中類型轉(zhuǎn)換在錯(cuò)誤處理中的應(yīng)用小結(jié)
隨著項(xiàng)目的進(jìn)展,關(guān)于Rust的故事又翻開(kāi)了新的一頁(yè),今天來(lái)到了服務(wù)器端的開(kāi)發(fā)場(chǎng)景,發(fā)現(xiàn)錯(cuò)誤處理中的錯(cuò)誤類型轉(zhuǎn)換有必要分享一下,對(duì)Rust錯(cuò)誤處理相關(guān)知識(shí)感興趣的朋友一起看看吧2023-09-09淺談Rust?+=?運(yùn)算符與?MIR?應(yīng)用
這篇文章主要介紹了Rust?+=?運(yùn)算符與?MIR?應(yīng)用,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-01-01Go調(diào)用Rust方法及外部函數(shù)接口前置
這篇文章主要為大家介紹了Go調(diào)用Rust方法及外部函數(shù)接口前置示例實(shí)現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06詳解Rust 生命周期符號(hào)使用的方法和規(guī)律
生命周期是 Rust 中處理引用和所有權(quán)的關(guān)鍵概念,通過(guò)正確使用生命周期符號(hào)和遵循相關(guān)規(guī)律,你可以編寫出安全、高效的 Rust 代碼,這篇文章主要介紹了Rust 生命周期符號(hào)使用的方法和規(guī)律,需要的朋友可以參考下2024-03-03Rust開(kāi)發(fā)環(huán)境搭建到運(yùn)行第一個(gè)程序HelloRust的圖文教程
本文主要介紹了Rust開(kāi)發(fā)環(huán)境搭建到運(yùn)行第一個(gè)程序HelloRust的圖文教程,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-12-12Rust循環(huán)控制結(jié)構(gòu)用法詳解
Rust提供了多種形式的循環(huán)結(jié)構(gòu),每種都適用于不同的場(chǎng)景,在Rust中,循環(huán)有三種主要的形式:loop、while和for,本文將介紹Rust中的這三種循環(huán),并通過(guò)實(shí)例展示它們的用法和靈活性,感興趣的朋友一起看看吧2024-02-02Rust聲明宏在不同K線bar類型中的應(yīng)用小結(jié)
在K線bar中,往往有很多不同分時(shí)k線圖,比如1,2,3,5,,,,,60,120,250,300…,,不同分鐘類型,如果不用宏,那么手寫會(huì)比較麻煩,下面就試用一下宏來(lái)實(shí)現(xiàn)不同類型的bar,感興趣的朋友一起看看吧2024-05-05