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