Rust中字符串String集合的具有使用
Rust開發(fā)者經(jīng)常被字符串困擾的原因
- 傾向于確保暴露出可能的錯(cuò)誤。
- 字符串是比很多程序員所想象的要更為復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。
- UTF-8。
字符串是什么
Rust 的核心語(yǔ)言中只有一種字符串類型:字符串 slice str,它通常以被借用的形式出現(xiàn),&str。
字符串(String)類型由 Rust 標(biāo)準(zhǔn)庫(kù)提供,而不是編入核心語(yǔ)言,它是一種可增長(zhǎng)、可變、可擁有、UTF-8 編碼的字符串類型。當(dāng) Rustaceans 提及 Rust 中的 "字符串 "時(shí),他們可能指的是 String 或 string slice &str 類型,而不僅僅是其中一種類型。
創(chuàng)建字符串
很多 Vec 可用的操作在 String 中同樣可用,事實(shí)上 String 被實(shí)現(xiàn)為一個(gè)帶有一些額外保證、限制和功能的字節(jié) vector 的封裝。其中一個(gè)同樣作用于 Vec<T> 和 String 函數(shù)的例子是用來(lái)新建一個(gè)實(shí)例的 new 函數(shù)
let mut s = String::new();
這新建了一個(gè)叫做 s 的空的字符串,接著我們可以向其中裝載數(shù)據(jù)。通常字符串會(huì)有初始數(shù)據(jù),因?yàn)槲覀兿M婚_始就有這個(gè)字符串。為此,可以使用 to_string 方法,它能用于任何實(shí)現(xiàn)了 Display trait 的類型,比如字符串字面值。
let data = "initial contents";
let s = data.to_string();
// 該方法也可直接用于字符串字面值:
let s = "initial contents".to_string();因?yàn)樽址畱?yīng)用廣泛,這里有很多不同的用于字符串的通用 API 可供選擇。其中一些可能看起來(lái)多余,不過(guò)都有其用武之地!在這個(gè)例子中,String::from 和 .to_string 最終做了完全相同的工作,所以如何選擇就是代碼風(fēng)格與可讀性的問(wèn)題了。
let s = String::from("initial contents");
記住字符串是 UTF-8 編碼的,所以可以包含任何可以正確編碼的數(shù)據(jù)
let hello = String::from("?????? ?????");
let hello = String::from("Dobry den");
let hello = String::from("Hello");
let hello = String::from("???????");
let hello = String::from("??????");
let hello = String::from("こんにちは");
let hello = String::from("?????");
let hello = String::from("你好");
let hello = String::from("Olá");
let hello = String::from("Здравствуйте");
let hello = String::from("Hola");
更新String
String 的大小可以增加,其內(nèi)容也可以改變,就像可以放入更多數(shù)據(jù)來(lái)改變 Vec 的內(nèi)容一樣。另外,可以方便的使用 + 運(yùn)算符或 format! 宏來(lái)拼接 String 值。
使用push_str和push附加字符串
可以通過(guò) push_str 方法來(lái)附加字符串 slice,從而使 String 變長(zhǎng):
let mut s = String::from("foo");
s.push_str("bar");
執(zhí)行這兩行代碼之后,s 將會(huì)包含 foobar。push_str 方法采用字符串 slice,因?yàn)槲覀儾⒉恍枰@取參數(shù)的所有權(quán)。
let mut s1 = String::from("foo");
let s2 = "bar";
s1.push_str(s2);
println!("s2 is {s2}");
push 方法被定義為獲取一個(gè)單獨(dú)的字符作為參數(shù),并附加到 String 中。
let mut s = String::from("lo");
s.push('l');
如何拼接字符串
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // 注意 s1 被移動(dòng)了,不能繼續(xù)使用
執(zhí)行完這些代碼之后,字符串 s3 將會(huì)包含 Hello, world!。s1 在相加后不再有效的原因,和使用 s2 的引用的原因,與使用 + 運(yùn)算符時(shí)調(diào)用的函數(shù)簽名有關(guān)。+ 運(yùn)算符使用了 add 函數(shù),這個(gè)函數(shù)簽名看起來(lái)像這樣:
fn add(self, s: &str) -> String {解釋:s2 使用了 &,意味著我們使用第二個(gè)字符串的 引用 與第一個(gè)字符串相加。這是因?yàn)?nbsp;add 函數(shù)的 s 參數(shù):只能將 &str 和 String 相加,不能將兩個(gè) String 值相加。不過(guò)等一下 —— &s2 的類型是 &String, 而不是 add 第二個(gè)參數(shù)所指定的 &str。
如果想要級(jí)聯(lián)多個(gè)字符串,+ 的行為就顯得笨重了:
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = s1 + "-" + &s2 + "-" + &s3;
這時(shí) s 的內(nèi)容會(huì)是 “tic-tac-toe”。在有這么多 + 和 " 字符的情況下,很難理解具體發(fā)生了什么。對(duì)于更為復(fù)雜的字符串鏈接,可以使用 format! 宏:
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{s1}-{s2}-{s3}");
索引字符串
在很多語(yǔ)言中,通過(guò)索引來(lái)引用字符串中的單獨(dú)字符是有效且常見的操作。然而在 Rust 中,如果你嘗試使用索引語(yǔ)法訪問(wèn) String 的一部分,會(huì)出現(xiàn)一個(gè)錯(cuò)誤。
let s1 = String::from("hello");
let h = s1[0];
錯(cuò)誤和提示說(shuō)明了全部問(wèn)題:Rust 的字符串不支持索引。那么接下來(lái)的問(wèn)題是,為什么不支持呢?為了回答這個(gè)問(wèn)題,我們必須先聊一聊 Rust 是如何在內(nèi)存中儲(chǔ)存字符串的。
內(nèi)部表示
String 是一個(gè) Vec<u8> 的封裝。
let hello = String::from("Hola");
在這里,len 的值是 4,這意味著儲(chǔ)存字符串 “Hola” 的 Vec 的長(zhǎng)度是四個(gè)字節(jié):這里每一個(gè)字母的 UTF-8 編碼都占用一個(gè)字節(jié)。那下面這個(gè)例子又如何呢?(注意這個(gè)字符串中的首字母是西里爾字母的 Ze 而不是數(shù)字 3。)
let hello = String::from("Здравствуйте");
我們已經(jīng)知道 answer 不是第一個(gè)字符 3。當(dāng)使用 UTF-8 編碼時(shí),(西里爾字母的 Ze)З 的第一個(gè)字節(jié)是 208,第二個(gè)是 151,所以 answer 實(shí)際上應(yīng)該是 208,不過(guò) 208 自身并不是一個(gè)有效的字母。返回 208 可不是一個(gè)請(qǐng)求字符串第一個(gè)字母的人所希望看到的,不過(guò)它是 Rust 在字節(jié)索引 0 位置所能提供的唯一數(shù)據(jù)。用戶通常不會(huì)想要一個(gè)字節(jié)值被返回。即使這個(gè)字符串只有拉丁字母,如果 &"hello"[0] 是返回字節(jié)值的有效代碼,它也會(huì)返回 104 而不是 h。
字節(jié)、標(biāo)量值、字形簇
比如這個(gè)用梵文書寫的印度語(yǔ)單詞 “??????”,最終它儲(chǔ)存在 vector 中的 u8 值看起來(lái)像這樣:
[224, 164, 168, 224, 164, 174, 224, 164, 184, 224, 165, 141, 224, 164, 164, 224, 165, 135]
這里有 18 個(gè)字節(jié),也就是計(jì)算機(jī)最終會(huì)儲(chǔ)存的數(shù)據(jù)。如果從 Unicode 標(biāo)量值的角度理解它們,也就像 Rust 的 char 類型那樣,這些字節(jié)看起來(lái)像這樣:
['?', '?', '?', '?', '?', '?']
這里有六個(gè) char,不過(guò)第四個(gè)和第六個(gè)都不是字母,它們是發(fā)音符號(hào)本身并沒(méi)有任何意義。最后,如果以字形簇的角度理解,就會(huì)得到人們所說(shuō)的構(gòu)成這個(gè)單詞的四個(gè)字母:
["?", "?", "??", "??"]
Rust 提供了多種不同的方式來(lái)解釋計(jì)算機(jī)儲(chǔ)存的原始字符串?dāng)?shù)據(jù),這樣程序就可以選擇它需要的表現(xiàn)方式,而無(wú)所謂是何種人類語(yǔ)言。
最后一個(gè) Rust 不允許使用索引獲取 String 字符的原因是,索引操作預(yù)期總是需要常數(shù)時(shí)間(O(1))。但是對(duì)于 String 不可能保證這樣的性能,因?yàn)?Rust 必須從開頭到索引位置遍歷來(lái)確定有多少有效的字符。
字符串切割slice
索引字符串通常是一個(gè)壞點(diǎn)子,因?yàn)樽址饕龖?yīng)該返回的類型是不明確的:字節(jié)值、字符、字形簇或者字符串 slice。因此,如果你真的希望使用索引創(chuàng)建字符串 slice 時(shí),Rust 會(huì)要求你更明確一些。為了更明確索引并表明你需要一個(gè)字符串 slice,相比使用 [] 和單個(gè)值的索引,可以使用 [] 和一個(gè) range 來(lái)創(chuàng)建含特定字節(jié)的字符串 slice:
let hello = "Здравствуйте"; let s = &hello[0..4];
這里,s 會(huì)是一個(gè) &str,它包含字符串的頭四個(gè)字節(jié)。早些時(shí)候,我們提到了這些字母都是兩個(gè)字節(jié)長(zhǎng)的,所以這意味著 s 將會(huì)是 “Зд”。
如果獲取 &hello[0..1] 會(huì)發(fā)生什么呢?答案是:Rust 在運(yùn)行時(shí)會(huì) panic,就跟訪問(wèn) vector 中的無(wú)效索引時(shí)一樣。你應(yīng)該小心謹(jǐn)慎地使用這個(gè)操作,因?yàn)檫@么做可能會(huì)使你的程序崩潰。
遍歷string
操作字符串每一部分的最好的方法是明確表示需要字符還是字節(jié)。對(duì)于單獨(dú)的 Unicode 標(biāo)量值使用 chars 方法。對(duì) “Зд” 調(diào)用 chars 方法會(huì)將其分開并返回兩個(gè) char 類型的值,接著就可以遍歷其結(jié)果來(lái)訪問(wèn)每一個(gè)元素了:
for c in "Зд".chars() {
println!("{c}");
}
另外 bytes 方法返回每一個(gè)原始字節(jié),這可能會(huì)適合你的使用場(chǎng)景:
for b in "Зд".bytes() {
println!("");
}
不過(guò)請(qǐng)記住有效的 Unicode 標(biāo)量值可能會(huì)由不止一個(gè)字節(jié)組成。
字符串不簡(jiǎn)單
總而言之,字符串還是很復(fù)雜的。不同的語(yǔ)言選擇了不同的向程序員展示其復(fù)雜性的方式。Rust 選擇了以準(zhǔn)確的方式處理 String 數(shù)據(jù)作為所有 Rust 程序的默認(rèn)行為,這意味著程序員們必須更多的思考如何預(yù)先處理 UTF-8 數(shù)據(jù)。這種權(quán)衡取舍相比其他語(yǔ)言更多的暴露出了字符串的復(fù)雜性,不過(guò)也使你在開發(fā)周期后期免于處理涉及非 ASCII 字符的錯(cuò)誤。
到此這篇關(guān)于Rust中字符串String集合的具有使用的文章就介紹到這了,更多相關(guān)Rust 字符串String內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Rust錯(cuò)誤處理之`foo(...)?`的用法與錯(cuò)誤類型轉(zhuǎn)換小結(jié)
foo(...)?語(yǔ)法糖為Rust的錯(cuò)誤處理提供了極大的便利,通過(guò)結(jié)合map_err方法和From?trait的實(shí)現(xiàn),你可以輕松地處理不同類型的錯(cuò)誤,并保持代碼的簡(jiǎn)潔性和可讀性,這篇文章主要介紹了Rust錯(cuò)誤處理:`foo(...)?`的用法與錯(cuò)誤類型轉(zhuǎn)換,需要的朋友可以參考下2024-05-05
Rust實(shí)現(xiàn)構(gòu)建器模式和如何使用Bon庫(kù)中的構(gòu)建器
這篇文章主要介紹了Rust實(shí)現(xiàn)構(gòu)建器模式和如何使用Bon庫(kù)中的構(gòu)建器,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-08-08
Rust開發(fā)環(huán)境搭建到運(yùn)行第一個(gè)程序HelloRust的圖文教程
本文主要介紹了Rust開發(fā)環(huán)境搭建到運(yùn)行第一個(gè)程序HelloRust的圖文教程,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-12-12
rust多個(gè)mod文件引用和文件夾mod使用注意事項(xiàng)小結(jié)
在 Rust 項(xiàng)目中,可以使用 mod 關(guān)鍵字將一個(gè)文件夾或一個(gè) rs 文件作為一個(gè)模塊引入到當(dāng)前文件中,本文給大家介紹rust多個(gè)mod文件引用和文件夾mod使用注意事項(xiàng)小結(jié),感興趣的朋友跟隨小編一起看看吧2024-03-03

