網站首頁 編程語言 正文
String
字符串是比很多開發者所理解的更為復雜的數據結構。加上 UTF-8 的不定長編碼等原因,Rust 中的字符串并不如其它語言中那么好理解。
Rust 的核心語言中只有一種字符串類型:str
。字符串 slice,它通常以被借用的形式出現:&str
是一些儲存在別處的 UTF-8 編碼字符串數據的引用。而 String
的類型是由標準庫提供的,而沒有寫進核心語言部分,它是可增長的、可變的、有所有權的、UTF-8 編碼的字符串類型。
?? Rust 標準庫中還包含一系列其他字符串類型,比如 OsString
、OsStr
、CString
和 CStr
。相關庫 crate 還會提供更多儲存字符串數據的數據類型。這些字符串類型能夠以不同的編碼,或者內存表現形式上以不同的形式,來存儲文本內容。
新建字符串
- String::new()函數
- to_string()方法
let data = "initial contents"; let s = data.to_string(); // 或者 let s = String::from(data); // 該方法也可直接用于字符串字面量: let s = "initial contents".to_string();
更新字符串
String
的大小可以增加,其內容也可以改變。另外,還可以使用 +
運算符或 format!
宏來拼接 String
值。
- push_str()
- push()
let mut s = String::from("foo"); let t = String::from("bar"); s.push_str(&t); // push 方法被定義為獲取一個單獨的字符作為參數,并附加到 String 中 let mut l = String::from("lo"); l.push('l');
?? pub fn push_str(&mut self, string: &str)
方法不會獲得字符串的所有權。另外值得一提的是,t
是 &String
類型,而 push_str
方法需要的是 &str
類型的參數。為什么這段代碼能夠正常編譯呢?這里就涉及到了 解引用強制轉換(deref coercion),我們將在后面的文章中介紹它。
使用 +
運算符或 format!
宏拼接字符串
let s1 = String::from("Hello, "); let s2 = String::from("world!"); let s3 = s1 + &s2;
你可以把他理解成 C++ 的運算符重載。在 Rust 中, +
的實現可能是 fn add(self, s: &str) -> String
這樣一個方法。
?? s1
的所有權將被移動到 add
調用中
如果想要級聯多個字符串,使用 +
就變得麻煩了。這時候可以使用 format!
宏:
let s1 = String::from("hello"); let s2 = String::from("the"); let s3 = String::from("world"); let s = format!("{}-{}-{}", s1, s2, s3);
索引字符串
在其他語言中,通過索引來引用字符串中的某個單獨字符是很常見的操作。但在 Rust 中,你可能會遇到問題:
這主要是因為:
- UTF-8 是不定長編碼,而 String 的實現是基于
Vec<u8>
的封裝:數組中每一個元素都是一個字節,但 UTF-8 中每一個漢字(或字符)都可能由一到四個字節組成 - 索引操作預期總是需要常數時間 (O(1))。但是對于
String
不可能保證這樣的性能,因為 Rust 必須從開頭到索引位置遍歷來確定有多少有效的字符。
字符串 slice
如果你真的希望使用索引創建字符串 slice 時,Rust 會要求你明確字符串范圍。這時你需要一個字符串 slice,使用 []
和一個 range 來創建含特定字節的字符串 slice:
fn main() { let s1 = String::from("你好,"); println!("{}", &s1[0..3]); // 你 }
如果獲取 &s1[0..1]
,Rust 在運行時會 panic。因此,你應該謹慎地使用這個操作,因為這么做可能會使你的程序崩潰。
遍歷字符串
可以使用 chars() 方法獲取該字符串的字母數組。
fn main() { let s1 = String::from("你好,"); let s2 = String::from("世界!"); let s3 = s1 + &s2; for char in s3.chars() { println!("{}", char); } }
HashMap
另外一個常用集合類型是 哈希 map(hash map)。HashMap<K, V>
類型儲存了一個鍵類型 K
對應一個值類型 V
的映射。它通過一個 哈希函數(hashing function)來實現映射,決定如何將鍵和值放入內存中。
哈希 map 適用于需要任何類型作為鍵來尋找數據的情況,而不是像 vector 那樣通過索引。
新建 HashMap
使用new
創建一個空的 HashMap
,并使用 insert
增加元素:
use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert(String::from("Blue"), 10); scores.insert(String::from("Yellow"), 50);
?? 必須首先 use
標準庫中集合部分的 HashMap
。在這上面介紹的三個常用集合中,HashMap
是最不常用的,所以并沒有被 prelude 自動引用。標準庫中對 HashMap
的支持也相對較少,例如,并沒有內建的構建宏。
?? 像 vector 一樣,哈希 map 將它們的數據儲存在堆上;哈希 map 是同質的:所有的鍵必須是相同類型,值也必須都是相同類型。
另一個構建哈希 map 的方法是使用一個元組的 vector 的 collect
方法:
use std::collections::HashMap; let teams = vec![String::from("Blue"), String::from("Yellow")]; let initial_scores = vec![10, 50]; let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect();
HashMap 和 ownership
- 對于像
i32
這樣的實現了Copy
trait 的類型,其值可以拷貝進哈希 map。 - 對于像
String
這樣擁有所有權的值,其值將被移動而哈希 map 會成為這些值的所有者。
use std::collections::HashMap; let field_name = String::from("Favorite color"); let field_value = String::from("Blue"); let mut map = HashMap::new(); map.insert(field_name, field_value); // 此時 field_name 和 field_value 被移動到了 map 中
?? 如果將值的引用插入哈希 map,這些值本身將不會被移動進哈希 map。但是這些引用指向的值必須至少在哈希 map 有效時也是有效的。此時就涉及到生命周期的內容。
訪問 HashMap 中的值
可以通過 get
方法并提供對應的鍵來從哈希 map 中獲取值:
use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert(String::from("Blue"), 10); scores.insert(String::from("Yellow"), 50); let team_name = String::from("Blue"); let score = scores.get(&team_name);
get
返回 Option<V>
,所以結果被裝進 Some
;如果某個鍵在哈希 map 中沒有對應的值,get
會返回 None
。當獲取到結果后,就需要使用到 match
進行匹配。
更新 HashMap
在更新前,我們需要考慮以下幾種情況:
- 已有
key-value
,直接覆蓋
- 只在沒有
key-value
時插入
- 利用已有
key-value
來更新
直接覆蓋
insert()
方法:
use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert(String::from("Blue"), 10); scores.insert(String::from("Blue"), 25); println!("{:?}", scores);
新插入
利用 entry()
函數返回的枚舉值,調用 or_insert()
方法進行處理:
use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert(String::from("Blue"), 10); scores.entry(String::from("Yellow")).or_insert(50); scores.entry(String::from("Blue")).or_insert(50); println!("{:?}", scores);
?? Entry
的 or_insert
方法在鍵對應的值存在時就返回這個值的可變引用,如果不存在則將參數作為新值插入并返回新值的可變引用。
更新舊值
or_insert
方法事實上會返回這個鍵的值的一個可變引用(&mut V
):
// 統計字符串中某個單詞的出現次數 use std::collections::HashMap; let text = "hello world wonderful world"; let mut map = HashMap::new(); for word in text.split_whitespace() { let count = map.entry(word).or_insert(0); // 之前不存在對應關系就初始化并置計數器為0 *count += 1; // 每次計數器加一 } println!("{:?}", map);
?? 這里我們將這個可變引用儲存在 count
變量中,所以為了賦值必須首先使用星號( *
)解引用 count
。這個可變引用在 for
循環的結尾離開作用域,這樣所有這些改變都是安全的并符合借用規則。
總結
原文鏈接:https://juejin.cn/post/7152424705809448997
相關推薦
- 2023-04-01 react組件實例屬性state詳解_React
- 2022-10-13 windows?server設置FTP域用戶隔離的方法_FTP服務器
- 2023-03-28 Python表示當前時間的方法合集_python
- 2022-10-12 pandas?round方法保留兩位小數的設置實現_python
- 2022-06-22 C語言詳解Z字形變換排列的實現_C 語言
- 2023-02-12 Jupyter?Notebook運行代碼無反應問題及解決方法_python
- 2023-05-22 python使用ctypes調用第三方庫時出現undefined?symbol錯誤詳解_python
- 2022-10-01 使用C++實現插件模式時的避坑要點(推薦)_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支