Rust中字符串類型&str和String的使用
概述
在Rust中,字符串是一種非常重要的數(shù)據(jù)類型,用于處理文本數(shù)據(jù)。Rust的字符串是以UTF-8編碼的字節(jié)序列,主要有兩種類型:&str和String。其中,&str是一個(gè)對(duì)字符數(shù)據(jù)的不可變引用,更像是對(duì)現(xiàn)有字符串?dāng)?shù)據(jù)的“視圖”,而String則是一個(gè)獨(dú)立、可變更的字符串實(shí)體。
&str和String
&str和String是Rust中兩種主要的字符串類型,它們?cè)谝韵?個(gè)方面存在比較明顯的區(qū)別。
所有權(quán)和可變性
- &str:是Rust核心語(yǔ)言中唯一的字符串類型,它是一個(gè)不可變的字符串切片,是對(duì)字符串?dāng)?shù)據(jù)的引用,并不擁有數(shù)據(jù)的所有權(quán)。&str可以安全地使用,但它的內(nèi)容是不可變的,也就是說(shuō),不能改變它指向的字符串的內(nèi)容。&str可以指向String的內(nèi)容,也可以指向靜態(tài)字符串字面量。
- String:這是一個(gè)在堆上分配的、可變的字符串類型。String類型由Rust標(biāo)準(zhǔn)庫(kù)提供,而不是編入核心語(yǔ)言。它擁有其內(nèi)容的所有權(quán),這意味著String可以被修改。String本質(zhì)上是一個(gè)封裝了動(dòng)態(tài)大小數(shù)組(Vec<u8>)的結(jié)構(gòu)體,該數(shù)組存儲(chǔ)了UTF-8編碼的字節(jié)。
生命周期
- &str:生命周期取決于它的來(lái)源。如果是字符串字面量,則生命周期為'static。如果來(lái)自某個(gè)作用域內(nèi)的String或其他類型,則其生命周期與該作用域相同。
- String:沒有明確的生命周期限制,只要String實(shí)例存在,它就可以被使用。
存儲(chǔ)位置
- &str:可能是指向靜態(tài)內(nèi)存中的字符串字面量(&'static str),比如:編譯時(shí)確定的常量字符串。也可能是指向堆上分配的String的一部分,或者任何其他類型的UTF-8編碼數(shù)據(jù)的區(qū)域。
- String:始終在堆上動(dòng)態(tài)分配。
性能
- &str:由于它只是一個(gè)引用,沒有額外的內(nèi)存分配成本,因此在某些情況下可能更高效。
- String:由于它在堆上分配,因此會(huì)有額外的內(nèi)存分配和復(fù)制成本,尤其是在字符串拼接時(shí)。
使用場(chǎng)景
- &str:當(dāng)只需要讀取字符串內(nèi)容,或者想要避免額外的內(nèi)存分配時(shí),使用&str。此外,在函數(shù)參數(shù)中,使用&str可以允許函數(shù)接受不同類型的字符串參數(shù),包括:String和靜態(tài)字符串字面量。
- String:當(dāng)需要一個(gè)可變的字符串,或者不關(guān)心字符串的具體來(lái)源時(shí),使用String。
與C/C++語(yǔ)言的比較
- &str:類似于C語(yǔ)言中的const char *,它只是一個(gè)指向字符串?dāng)?shù)據(jù)的指針,并不擁有數(shù)據(jù)。在Rust中,&str比C語(yǔ)言中的裸指針更安全,因?yàn)樗幸粋€(gè)生命周期參數(shù)來(lái)確保引用的有效性。
- String:類似于C++中的std::string,是一個(gè)字符的容器,并且擁有其內(nèi)容。
字符串的創(chuàng)建
在Rust中,創(chuàng)建字符串有多種方法。根據(jù)具體需求,我們可以選擇不同的方法。如果需要一個(gè)可變的字符串并且打算在程序運(yùn)行時(shí)修改它,那么String類型是最佳選擇。如果只是需要一個(gè)對(duì)靜態(tài)文本的引用,那么&str就足夠了。
使用字符串字面量創(chuàng)建&str
字符串字面量是在代碼中直接寫入的文本,它們被存儲(chǔ)在程序的只讀數(shù)據(jù)段中,并且是不可變的。字符串字面量隱式地具有&str類型。在下面的示例代碼中,text是一個(gè)指向字符串字面量的引用,其類型為&str。
let text: &str = "Hello, CSDN";
使用String::new創(chuàng)建空的String
如果我們想要一個(gè)可變的、可以增長(zhǎng)的字符串,應(yīng)該使用String類型。在下面的示例代碼中,empty_str是一個(gè)空的String變量,我們可以向其中添加內(nèi)容。
fn main() { let mut empty_str = String::new(); empty_str.push_str("Hello"); println!("{}", empty_str); }
使用字符串字面量初始化String
可以直接將字符串字面量轉(zhuǎn)換為String,這是通過(guò)調(diào)用to_string方法或to_owned方法來(lái)實(shí)現(xiàn)的。
fn main() { let text1 = "Hello, CSDN".to_string(); let str_slice: &str = "Hello, Rust"; let text2 = str_slice.to_owned(); println!("{}", text1); println!("{}", text2); }
使用format!宏創(chuàng)建String
format!宏是Rust中創(chuàng)建格式化字符串的強(qiáng)大工具,它可以根據(jù)提供的格式字符串和參數(shù)生成一個(gè) String。
fn main() { let name: &str = "CSDN"; let info = format!("Hello, {}", name); println!("{}", info); }
使用String::from創(chuàng)建String
String::from是一個(gè)便利的方法,用于從實(shí)現(xiàn)了Into<String>特征的任何類型創(chuàng)建String。因?yàn)樽址置媪侩[式地實(shí)現(xiàn)了這個(gè)特征,故可以直接使用。
let text = String::from("Hello, CSDN");
字符串的拼接
Rust提供了強(qiáng)大的字符串拼接功能,可以讓字符串操作變得更加靈活和高效。
使用+運(yùn)算符或+=運(yùn)算符
如果想要將兩個(gè)String類型進(jìn)行拼接,可以使用+運(yùn)算法。
fn main() { let str1 = String::from("Hello"); let str2 = String::from(" CSDN"); // 不能直接使用str1 + str2 let str = str1 + &str2; println!("{}", str); // 編譯錯(cuò)誤:value borrowed here after move println!("{}", str1); }
在上面的示例代碼中,我們將str1和str2進(jìn)行了拼接,并得到了str。拼接時(shí),我們使用了&str2,而沒有直接使用str2。拼接完成后,str1不再有效。之所以會(huì)這樣,與使用+運(yùn)算符時(shí)調(diào)用的函數(shù)簽名有關(guān)。Rust的+運(yùn)算符使用了add函數(shù),其簽名與下面的函數(shù)聲明類似。
fn add(self, s: &str) -> String
首先,str2使用了&,意味著我們使用第二個(gè)字符串的引用與第一個(gè)字符串相加。這是因?yàn)閍dd函數(shù)只能將&str和String相加,而不能將兩個(gè)String值相加。在Rust中,可以通過(guò)Deref強(qiáng)制轉(zhuǎn)換將&String強(qiáng)轉(zhuǎn)成&str,相當(dāng)于自動(dòng)把&str2變成了&str2[..]。其次,add函數(shù)直接獲取了self的所有權(quán),因?yàn)閟elf沒有使用&。這意味著,str1的所有權(quán)被移動(dòng)到add函數(shù)后,str1將不再有效。
若要對(duì)可變的String進(jìn)行拼接操作,還可以使用+=操作符。但實(shí)際上,這并不是簡(jiǎn)單的連接,而是創(chuàng)建了一個(gè)新的String實(shí)例,并丟棄了原String分配的內(nèi)存。
fn main() { let mut str1 = String::from("Hello"); let str2 = " CSDN"; str1 += str2; println!("{}", str1); }
注意:使用+=運(yùn)算符,或者連續(xù)使用+運(yùn)算符進(jìn)行多次拼接,會(huì)導(dǎo)致多次內(nèi)存分配,效率較低,尤其是在處理大量數(shù)據(jù)時(shí)。如果需要高效地拼接多個(gè)字符串,建議使用下面的format!宏。
使用format!宏
format!宏是一種更靈活且高效的字符串拼接方法,尤其適用于包含變量和格式化文本的情況。format!宏可以處理各種復(fù)雜的格式化需求,并且它的性能通常優(yōu)于簡(jiǎn)單的+拼接。
fn main() { let name: &str = "CSDN"; let info = format!("Hello, {}", name); println!("{}", info); }
使用push_str方法或push方法
如果已經(jīng)有了一個(gè)String變量,并且想要將另一個(gè)字符串或字符追加到它后面,可以使用push_str方法或push方法。注意:push系列方法不會(huì)創(chuàng)建新的String實(shí)例,而是直接在原有的String緩沖區(qū)上追加內(nèi)容,這通常比使用+運(yùn)算符更高效。
fn main() { let mut text = String::from("Hello "); text.push_str("Rust"); println!("{}", text); text.push(' '); text.push('C'); text.push('S'); text.push('D'); text.push('N'); println!("{}", text); }
字符串的搜索與替換
在Rust中,我們可以使用find、rfind、contains、replace等方法來(lái)進(jìn)行字符串的搜索與替換。在下面的示例代碼中,我們首先調(diào)用find方法查找子串"CSDN",并返回一個(gè)Option類型的值。接下來(lái),我們調(diào)用contains方法來(lái)檢查text字符串是否包含了子串"Hello",若包含,返回true,否則返回false。最后,我們調(diào)用replace方法來(lái)替換字符串中的子串。
replace方法接收兩個(gè)參數(shù):第一個(gè)參數(shù)是要被替換的子串,第二個(gè)參數(shù)是替換后的新子串。該方法會(huì)返回一個(gè)新的字符串,其中所有與給定模式匹配的子串都被替換為指定的替換字符串。注意:第一個(gè)參數(shù)中的原始字符串不會(huì)被修改。
fn main() { let text = "Hello CSDN"; // 搜索子串 let index = text.find("CSDN"); if let Some(value) = index { println!("found: {}", value); } else { println!("not found"); } // 包含子串 let contain_hello = text.contains("Hello"); println!("contain hello: {}", contain_hello); // 替換子串 let replaced: String = text.replace("CSDN", "GitHub"); println!("{}", replaced); }
字符串的長(zhǎng)度
在Rust中,獲取字符串的長(zhǎng)度是一個(gè)常見的操作。Rust的String類型提供了一個(gè)len方法,可以用來(lái)獲取字符串中字節(jié)的數(shù)量。需要特別注意的是:這個(gè)長(zhǎng)度是以字節(jié)為單位的,對(duì)于ASCII字符串來(lái)說(shuō),每個(gè)字符占用一個(gè)字節(jié);但是,對(duì)于包含多字節(jié)字符(比如:UTF-8編碼的Unicode字符)的字符串,len方法返回的是字節(jié)的總數(shù),而不是字符的總數(shù)。
如果想要獲取字符串中Unicode字符的數(shù)量,我們應(yīng)該使用chars方法,然后計(jì)算迭代器中元素的數(shù)量。chars方法會(huì)返回一個(gè)迭代器,該迭代器逐個(gè)產(chǎn)生字符串中的Unicode字符。
fn main() { let text = "Hello 霸都"; // 獲取字節(jié)長(zhǎng)度 let byte_len = text.len(); // 輸出:12 println!("{}", byte_len); // 獲取字符長(zhǎng)度 let char_len = text.chars().count(); // 輸出:8 println!("{}", char_len); }
另外,Rust字符串不支持直接通過(guò)索引來(lái)訪問(wèn)單個(gè)字符。這是因?yàn)?,UTF-8編碼格式下,單個(gè)字符可能占用1到4個(gè)字節(jié),索引操作會(huì)帶來(lái)潛在的非確定性和不一致性問(wèn)題。如果確實(shí)需要通過(guò)索引訪問(wèn)字符,可以使用chars()方法。它會(huì)返回一個(gè)迭代器,產(chǎn)生字符串中的每個(gè)Unicode字符。然后,我們可以使用nth方法或者其他集合方法來(lái)獲取特定位置的字符。
fn main() { let text = "Hello 霸都"; // 注意:索引從0開始計(jì)數(shù) let index = 6; let cur_char = text.chars().nth(index); // 輸出:index 6: 霸 match cur_char { Some(c) => println!("index {}: {}", index, c), None => println!("index out of bounds"), } }
字符串與字節(jié)的轉(zhuǎn)換
Rust中的字符串和字節(jié)之間可以方便地進(jìn)行轉(zhuǎn)換,這在處理二進(jìn)制數(shù)據(jù)和編解碼時(shí)非常有用。
fn main() { let text = "Hello CSDN"; // 字符串轉(zhuǎn)字節(jié) let bytes = text.as_bytes(); // 輸出:[72, 101, 108, 108, 111, 32, 67, 83, 68, 78] println!("{:?}", bytes); // 字節(jié)轉(zhuǎn)字符串 let bytes2 = [72, 101, 108, 108, 111]; let text2 = std::str::from_utf8(&bytes2).unwrap(); // 輸出:Hello println!("{}", text2); }
總結(jié)
由于Rust強(qiáng)調(diào)安全性與內(nèi)存管理,它的字符串設(shè)計(jì)也體現(xiàn)出了這一點(diǎn):不可變的&str確保了引用安全,而String則通過(guò)所有權(quán)系統(tǒng)保證了內(nèi)存的有效管理,避免了懸垂引用和其他常見的內(nèi)存錯(cuò)誤。
到此這篇關(guān)于Rust中字符串類型&str和String的使用的文章就介紹到這了,更多相關(guān)Rust &str String內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Rust語(yǔ)言之trait中的個(gè)方法可以重寫嗎
在Rust中,trait定義了一組方法,這些方法可以被一個(gè)或多個(gè)類型實(shí)現(xiàn),當(dāng)你為某個(gè)類型實(shí)現(xiàn)一個(gè)trait時(shí),你可以為該trait中的每個(gè)方法提供自己的具體實(shí)現(xiàn),本文將給大家介紹一下trait中的個(gè)方法是否可以重寫,需要的朋友可以參考下2023-10-10Rust使用Channel實(shí)現(xiàn)跨線程傳遞數(shù)據(jù)
消息傳遞是一種很流行且能保證安全并發(fā)的技術(shù),Rust也提供了一種基于消息傳遞的并發(fā)方式,在rust里使用標(biāo)準(zhǔn)庫(kù)提供的Channel來(lái)實(shí)現(xiàn),下面我們就來(lái)學(xué)習(xí)一下如何使用Channel實(shí)現(xiàn)跨線程傳遞數(shù)據(jù)吧2023-12-12詳解Rust編程中的共享狀態(tài)并發(fā)執(zhí)行
雖然消息傳遞是一個(gè)很好的處理并發(fā)的方式,但并不是唯一一個(gè),另一種方式是讓多個(gè)線程擁有相同的共享數(shù)據(jù),本文給大家介紹Rust編程中的共享狀態(tài)并發(fā)執(zhí)行,感興趣的朋友一起看看吧2023-11-11Rust應(yīng)用調(diào)用C語(yǔ)言動(dòng)態(tài)庫(kù)的操作方法
這篇文章主要介紹了Rust應(yīng)用調(diào)用C語(yǔ)言動(dòng)態(tài)庫(kù),本文記錄了筆者編寫一個(gè)簡(jiǎn)單的C語(yǔ)言動(dòng)態(tài)庫(kù),并通過(guò)Rust調(diào)用動(dòng)態(tài)庫(kù)導(dǎo)出的函數(shù),需要的朋友可以參考下2023-01-01rust交叉編譯問(wèn)題及報(bào)錯(cuò)解析
這篇文章主要為大家介紹了rust交叉編譯問(wèn)題及報(bào)錯(cuò)解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07Rust中non_exhaustive的enum使用確保程序健壯性
這篇文章主要為大家介紹了Rust中non_exhaustive的enum使用確保程序健壯性示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11