Rust 入坑指南:鱗次櫛比|CSDN 博文精選

2020-12-11 CSDN

作者 | Jackyzhe

責編 | 屠敏

很久沒有挖Rust的坑啦,今天來挖一些排列整齊的坑。沒錯,就是要介紹一些集合類型的數據類型。「鱗次櫛比」這個標題是不是顯得很有文化?

在Rust入坑指南:常規套路(https://blog.csdn.net/K_Ohaha/article/details/102481562)一文中我們已經介紹了一些基本數據類型了,它們都存儲在棧中,今天我們重點介紹3種數據類型:string,vector和hash map。

String

String類型我們在之前的學習中已經有了較多的接觸,但是沒有進行過詳細的介紹。有些有編程基礎的同學可能不屑於學習String類型,畢竟它在所有程式語言中可以說是最常用的類型了,大家也都很熟悉了。對於有這種心理的同學,我想對他們說:我剛開始也是這樣想的,直到後來我被編譯器揍的滿頭包,才下定決心回來認真學習一下String類型。

Rust的字符串分為以下幾種類型:

str:表示固定長度的字符串String:表示可增長的字符串CStr:表示由C分配,被Rust借用的字符串,一般用於和C語言交互CString:表示由Rust分配並且可以傳遞給C語言的字符串OsStr:表示和作業系統相關的字符串,主要為了兼容WindowsOsString:OsStr的可變版本Path:表示路徑PathBuf:是Path的可變版本本文我們重點討論前兩種,因為它們是開發過程中最常用的,也是比較容易混淆的。對於str,我們常見的是它的引用類型,&str。如果你看過了Rust入坑指南:核心概念一文後,相信你已經了解了引用類型和Ownership的概念。也就是說String類型具有Ownership而&str沒有。

在Rust中,String本質上是Vec<u8>,Vec是向量集合的關鍵字,我們在後面會介紹。String類型由三個部分組成,分別是:指向堆中字節序列的指針,記錄堆中字節序列的長度和堆分配的容量。通過一段代碼也許你很有更深的理解。

fn main() {let mut a = String::from("foo"); println!("{:p}", a.as_ptr()); println!("{:p}", &a); assert_eq!(a.len(), 3); a.reserve(10); assert_eq!(a.capacity(), 13);}

在這段代碼中我們可以看到,a.as_ptr()獲取指針和&a獲取的指針是不一樣的。

這裡我們解釋一下,as_ptr獲取到的指針是堆中字節序列的指針地址,而&a的地址是字符串變量在棧上的指針地址。另外,len()和capacity()方法得到的長度都是字節數量,而非字符數量。這裡你可以自己動手試試中文字符的長度。

聊完了字符串的基本概念以後,相信你已經對Rust的字符串有了一個大概的認識。接下來我們就一起來看一看字符串的CRUD的方法吧。

創建字符串

話不多說,先來展示一下創建字符串的各種方法。

fn main() {letstring: String = String::new();letstring: String = String::from("hello rust");letstring: String = String::with_capacity(10);let str: &'static str = "Jackey";letstring: String = str.to_owned();letstring: String = str.to_string();}

我們比較常用的是前兩種,下面介紹一下後面幾個方法。with_capacity()是創建一個空字符串,參數表示在堆中分配的字節數。to_owned和to_string是演示了如何把&str類型轉換成String類型。

修改字符串

Rust修改字符串的常用方法也有很多,例如在字符串後追加,連接兩個字符串,更新字符串等。下面這段代碼就展示了一些修改字符串的方法。

fn main() {let mut hello = String::from("Hello, "); hello.push('J'); // 追加單個字符 hello.push_str("ackey! "); //追加字符串 println!("push: {}", hello); hello.extend(['M', 'y', ' '].iter()); //追加多個字符,參數為迭代器 hello.extend("name".chars()); println!("extend: {}", hello); hello.insert(0, 'h'); //類似於push,可以指定插入的位置 hello.insert(1, 'a'); hello.insert(2, '!'); hello.insert_str(0, "Haha"); println!("insert: {}", hello);let left = "Hello, ".to_string();let right = "World".to_string();let result = left + &right; println!("+: {}", result); //使用+連接字符串時,第二個必須為引用let mut message = "rust".to_string(); //使用+=連接字符串時,字符串必須定義為可變 message += "!"; println!("+=: {}", message);let s = String::from("foobar");let s: String = s .chars() .enumerate() .map(|(_i, c)| {c.to_uppercase().to_string()}) .collect(); println!("update chars: {}", s);let s1 = String::from("hello");let s2 = String::from("rust");let s3 = format!("{}-{}", s1, s2); println!("format: {}", s3);}

我們對上面的代碼做一些補充的解釋。

push和insert類似,帶有_str的方法接收的參數是字符串,否則只能接收單個字符。insert可以指定插入的位置,而push只能在字符串末尾插入。

使用「+」連接字符串時,第一個參數是String類型,第二個則需要是引用類型&str。這類似於我們調用一個add方法,它的定義是這樣的:

所以,第一個參數的ownership轉移到了函數中,又通過返回結果傳遞出來。也就是說,在使用了+操作符之後,left已經沒有ownership了。

字符串查找

在Rust中,字符串是不能根據位置來獲取到指定字符的。也就是下面這段代碼是編譯不過的。

let s1 = String::from("hello");let h = s1[0];

因為,Rust會認為這個0是指第一個字節,而Rust字符串中的字符可能佔有多個字節(還記得前面我讓你用中文字符實驗代碼嗎?)所以,如果你單純的想要獲取一個字節,編譯器不知道你是真的想要獲取字節對應的數值,還是要獲取那個字符。

我們在處理字符串時通常有以下方法:

fn main() {let hello = "Здравствуйте";let s = &hello[0..4]; println!("{}", s);let chars = hello.chars();for c in chars { println!("{}", c); }let bytes = hello.bytes();for byte in bytes { println!("{}", byte); }letget = hello.get(0..1);let mut s = String::from("hello");let get_mut = s.get_mut(3..5);let message = String::from("hello-world");let (left, right) = message.split_at(6); println!("left: {}, right: {}", left, right);}

通常是使用字符切片,也可以使用chars方法獲取到Chars迭代器,然後可以對每個字符進行單獨處理。此外,使用get或get_mut方法也可以接收索引範圍,返回指定的字符串切片。返回結果是Option類型,這是因為如果指定的索引返回不能返回完整字符,那麼Rust就會返回None。這裡也可以使用is_char_boundary方法來判斷一個位置是否是非法邊界。

最後,也可以使用split_at或split_at_mut方法來分割字符串。這要求分割的位置正好是字符邊界位置,如果不是,程序就會崩潰。

刪除字符串

Rust的標準庫提供了一些刪除字符串的方法,我們來演示一些:

fn main() { let mut hello = String::from("hello"); hello.remove(3);println!("remove: {}", hello); hello.pop();println!("pop: {}", hello); hello.truncate(1);println!("truncate: {}", hello); hello.clear();println!("clear: {}", hello);}

結果如圖:

remove方法用來刪除字符串中的某個字符,其接收的參數是字符的起始位置,如果是不是某個字符的起始位置,會導致程序崩潰。

pop方法會彈出字符串末尾的字符,truncate方法是截取指定長度字符串,而clear方法則是用來清空字符串。

至此,關於Rust中的字符串的基本概念和CRUD我們都已經介紹完了,接下來我們再來看另一種集合類型Vector。

Vector

Vector是用來存儲相同數據類型的多個數據一種數據類型。它的關鍵字是Vec<T>。下面我們一起來看看向量的CRUD吧。

創建向量

fn main() {let v1: Vec<i32> = Vec::new();let v2 = vec![1, 2, 3];}

上面這段代碼演示了創建一個向量的兩種方式,第一種是使用new函數來創建一個空的向量,由於沒有添加元素,所以要顯式的指定存儲元素的類型。第二種是創建一個有初始值的向量集合,我們直接使用vec!宏,然後指定初始值即可,不需要指定向量中元素的數據類型,因為編譯器可以自己推斷出來。

更新向量

fn main(){ let mut v = Vec::new(); v.push(1); v.push(2);}

創建一個空的向量之後,如果我們想要增加元素,就可以直接使用push方法,向末尾追加元素。

刪除向量

fn main() {let mut v = Vec::new(); v.push(1); v.push(2); v.push(3); v.pop();for i in &v { println!("{}", i); }}

刪除單個元素可以使用pop方法,而要刪除整個向量,只能像其他結構體一樣,到其ownership失效。

讀取向量元素

fn main() {let v = vec![1, 2, 3, 4, 5];let third: &i32 = &v[2]; println!("The third element is {}", third); match v.get(2) { Some(third) => println!("The third element is {}", third), None => println!("There is no third element."), }let v = vec![100, 32, 57];for i in &v { println!("{}", i); }}

當你需要讀取單個指定元素時,有兩種方法可以用,一種是使用[],另一種是使用get方法。兩種方法的區別是:第一種返回的是元素的類型,而get返回的是Option類型。如果你指定的位置越界了,那麼使用第一種方法程序會直接崩潰,而使用第二種方法則會返回None。

此外,還可以通過遍歷向量的形式來讀取元素。如果想要存儲不同類型的數據,我們可以藉助枚舉類型。

fn main() { enum SpreadsheetCell {Int(i32), Float(f64), Text(String), }let row = vec![ SpreadsheetCell::Int(3), SpreadsheetCell::Text(String::from("blue")), SpreadsheetCell::Float(10.12), ];}

HashMap

HashMap存儲了KV結構的數據,各個Key必須是同一種類型,各個Value必須是同一種類型。由於HashMap是三種集合類型中使用最少的,所以在使用之前,需要手動引入進來

use std::collections::HashMap;

創建HashMap

首先我們來了解一下如何創建一個新的Hash Map並增加元素。

use std::collections::HashMap;fn main() {let field_name = String::from("Favorite color");let field_value = String::from("Blue");let mut map = HashMap::new(); map.insert(field_name, field_value);}

注意,在使用insert方法時,field_name和field_value都會失去所有權。那如何再使用它們呢?我們只能從Hash Map中再拿出來。

訪問Hash Map的數據

use std::collections::HashMap;fn main() {let field_name = String::from("Favorite color");let field_value = String::from("Blue");let mut map = HashMap::new(); map.insert(field_name, field_value);let favorite = String::from("Favorite color");let color = map.get(&favorite); match color { Some(x) => println!("{}", x), None => println!("None"), }}

可以看到,我們使用get可以獲取到指定Key的值,get方法返回的是Option類型,如果沒有指定的Value,則會返回None。此外,也可以使用for循環來遍歷Hash Map。

use std::collections::HashMap;fn main() {let mut scores = HashMap::new(); scores.insert(String::from("Blue"), 10); scores.insert(String::from("Yellow"), 50);for (key, value) in &scores { println!("{}: {}", key, value); }}

更新Hash Map

當我們向同一個Key insert值時,舊的值就會被覆蓋。如果只想要在Key不存在時插入,則可以使用entry。

use std::collections::HashMap;fn main() {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);}

總結

今天帶大家一起挖了三個坑,string,vector和hash map,分別介紹了每種數據類型的CRUD。對string的介紹佔了比較大的篇幅,因為它是最常用的數據類型之一。當然這部分的相關知識還有很多,歡迎大家和我一起學習交流。

聲明:本文為CSDN博主「Jackyzhe」的原創文章。

想為博主點讚?

想要請教博主?

相關焦點

  • | CSDN 博文精選
    1001次的條件下得到的結果沒有改進,這就說明不是迭代次數的問題,我們還是保持2001的迭代數,將學習率改為0.01,看效果:完美~~~最後損失降為0了~~一般來說,神經網絡中的超參中最重要的就是學習率了,如果損失一直降不下來,我們首先要想到修改學習率,其他的超參次之……大家可以觀察一下我們的預測值,四項分別對應[0,1,1,0],已經是相當接近了……原文:https://blog.csdn.net
  • Rust一周集錦(三):關閉3個RFC並為1.4版本做準備
    這是來自 http://this-week-in-rust.org/網站關於Rust的新的一周博文、活動、版本開發、貢獻者的集錦。在此,我們轉載了過來,以便國內Rust語言愛好者學習。精彩博文: 新版發布和項目更新: 新的Rust貢獻者名單: Alex OzdemirChris KrychoDylan McKayElaine "See More" NemoJonas SchievinkllogiqNathan KleynNicholas SeckarNiranjan PadmanabhanTim Neumannw00nsWithout
  • Rust 中對結構化數據編碼解碼 - Rust 實踐指南
    (package_info["package"]["name"].as_str(), Some("your_package")); Ok(())}因公眾號篇幅和體驗限制,以小端模式(低位模式)字節順序讀寫整數等實例請點擊底部「閱讀原文」,或者訪問 https://rust-cookbook.budshome.com,按照左側導航閱讀
  • Rust 中的錯誤處理 - Rust 實踐指南
    record 1 (line: 2, byte: 15): field 1: number too large to fit in target type└> 3 - field 1: number too large to fit in target type因公眾號篇幅和體驗限制,通過附加新的錯誤來擴展錯誤信息等實例請點擊底部「閱讀原文」,或者訪問 https://rust-cookbook.budshome.com
  • Rust入坑指南:海納百川
    最後留個預告,這個坑還沒完,我們下次繼續往深處挖。本文轉載自:https://www.cnblogs.com/Jackeyzhe/p/12199191.html,旨在分享技術,整合信息,若有侵權請私信,侵權必刪
  • Rust 語言新人入門指南
    這時,建議看Rust 官網  https://rust-lang.orgRust Force  https://rustforce.net  Rust 基本要素匯集Rust 語言中文社區論壇   https://rust.cc《Rust語言學習交流》公眾號,每日 Rust 新聞和知識推送知乎  有很多關於 Rust 相關的知識、專欄
  • |CSDN博文精選
    如果想要對 Hermes引擎中的知識進行深入講解 ,感興趣的同學可以繼續關注作者Carson_Ho的博客開發筆記(https://blog.csdn.net/carson_ho/article/details/96267553)。
  • 霹靂布袋戲新手入坑指南
    作為一個準備入坑的萌新該從哪看起呢?以下盤點了霹靂布袋戲的初級入坑指南,供各位參考。 1.《霹靂劍蹤》——最多好評的入坑劇《霹靂劍蹤》於2005年2月推出,總計30集。《刀說異數》劇情獨立便於入坑。《刀說異數》是為了慶賀素還真登場三十周年,根據1990年《霹靂異數》重製的新劇。霹靂老劇融入現代科技元素重出江湖,劇情緊湊,信息量含量大。霹靂系列劇中的三大主角素還真與葉小釵、一頁書紛紛登場,一眾經典人物也在劇中出現。
  • 新手入坑手帳指南,做手帳常用的幾種筆的選擇
    今天說一下新手入坑如何選擇手帳用的筆,我會分4個板塊介紹,一起來看看有沒有踩雷~(每周更新一期手帳入坑指南)手帳入坑指南之手帳用筆1、日常寫字用的筆▎原子筆當然筆的種類遠不止這些,大家新手入坑的話,選擇一些基礎款的筆就夠了,喜歡的小可愛記得給我點讚、評論、關注三連發,愛你們~
  • 計算機大數乘法引發的思考 | CSDN 博文精選
    有人問我在同樣O(nlogn) 的時間複雜度情況下,為什麼快速排序比歸併排序快,我沒有辦法證明,但是事實上的原因卻是非常顯然的:不知為不知–資訊理論和最大熵原則 :https://blog.csdn.net/dog250/article/details/78944526版權聲明:本文為CSDN博主「dog250」的原創文章。
  • CSDN博文精選:Android系列開發博客資源匯總
    這篇博文中所附帶的遊戲源碼包,是由十一款Applet小遊戲所組成。本系列文章初步的計劃是,由Android SDK Sample的貪吃蛇遊戲(Snake)為切入點,通過跟J2ME手機遊戲和J2SE桌面遊戲開發的比較,逐步介紹Android遊戲開發的相關知識和流行框架。前文簡要介紹了Android應用程式的Activity的啟動過程。
  • |CSDN 博文精選
    原文:https://blog.csdn.net/w605283073/article/details/98475057【END】
  • |CSDN博文精選
    作者:許文武,博客暱稱「天元浪子」,本文首發於作者CSDN博客https://blog.csdn.net/xufive/article/details/96475103。【End】熱 文 推 薦 全球 JavaScript 開發者薪酬揭曉,你拖後腿了嗎?性能比 GPU 高 100 倍!
  • 海洋堂AMAZING YAMAGUCHI(驚奇山口)系列入坑指南(下)
    本期文章是海洋堂AMAZING YAMAGUCHI(驚奇山口)系列入坑指南(下),已經是第三部了,如果想重溫第一部(上)和第二部(中)的話,請猛戳下面對應的圖片傳送門~上部主要介紹了海洋堂品牌的背景、山口關節、主要的設計師,也介紹了部分(美隊及之前編號的)AY系列的玩具。
  • | CSDN博文精選
    作者:許文武,博客暱稱「天元浪子」,本文首發於作者CSDN博客https://blog.csdn.net/xufive/article/details/96475103。【End】
  • 鋼筆墨水入坑輕度指南(一)
    寫在前面:我不是文具行業的從業者,所以這個指南是一個玩了一年左右的墨水小白用戶根據自己的經驗寫的,如果有啥專業性的磚
  • Rust-web 媒體類型(MIME)—— Rust 實踐指南
    ("MIME for {}: {}", file, mime); }}因公眾號篇幅和體驗限制,解析 HTTP 響應的 MIME 類型等實例請點擊底部「閱讀原文」,或者訪問 https://rust-cookbook.budshome.com,按照左側導航閱讀。
  • 奢侈品包包入坑指南 這幾條標準一定要參考
    因此為了買到合心意又好用的包包,這份奢侈品包包入坑指南一定要看一下:剛入門建議選擇一個經典款的包包:入坑奢侈包包之前預設自己的承受能力:所以在入坑之前一定要做好不同材質包包清理保養的難度和方法,根據自己的時間找出比較省心簡便的包包款式和材質入手。像是皮革的雖然版型好很有檔次,但打理起來卻是比較費事的,相對來說帆布款式可能更為省心。
  • |CSDN 博文精選
    我們趟過的坑,幾乎都是 dtype 挖的;我們的迷茫,幾乎都是因為 shape 和我們期望的不一樣;我們的工作,很多都是在改變 shape。想了解 numpy 支持的元素類型,請點擊《數學建模三劍客MSN》(https://blog.csdn.net/xufive/article/details/52449255)。
  • |CSDN 博文精選
    作者 | 平頭哥的技術博文責編 | 屠敏出品 | CSDN 博客String 對象的實現String對象是 Java 中使用最頻繁的對象之一,所以 Java 公司也在不斷的對String對象的實現進行優化,以便提升String對象的性能,看下面這張圖,一起了解一下String對象的優化過程。