亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

深入了解Rust的切片使用

 更新時(shí)間:2022年11月01日 10:56:14   作者:古明地覺  
除了引用,Rust?還有另外一種不持有所有權(quán)的數(shù)據(jù)類型:切片(slice),切片允許我們引用集合中某一段連續(xù)的元素序列,而不是整個(gè)集合。本文讓我們來深入了解Rust的切片

為什么要有切片

除了引用,Rust 還有另外一種不持有所有權(quán)的數(shù)據(jù)類型:切片(slice),切片允許我們引用集合中某一段連續(xù)的元素序列,而不是整個(gè)集合。

考慮這樣一個(gè)小問題:編寫一個(gè)搜索函數(shù),它接收字符串作為參數(shù),并將字符串中的首個(gè)單詞作為結(jié)果返回。如果字符串中不存在空格,那么就意味著整個(gè)字符串是一個(gè)單詞,直接返回整個(gè)字符串作為結(jié)果即可。

讓我們來看一下這個(gè)函數(shù)的簽名應(yīng)該如何設(shè)計(jì):

fn?first_word(s:?&String)?->??

由于我們不需要獲得傳入值的所有權(quán),所以這個(gè)函數(shù)采用了 &String 作為參數(shù)。但它應(yīng)該返回些什么呢?我們還沒有介紹獲取部分字符串的方法,但是可以曲線救國(guó),將首個(gè)單詞結(jié)尾處的索引返回給調(diào)用者。

fn?first_word(s:?&String)?->?usize?{
????let?bytes?=?s.as_bytes();
????for?(index,?&item)?in?bytes.iter().enumerate()?{
????????if?item?==?b'?'?{
????????????//?這里要使用?return?index;?不能只寫?index
????????????//?因?yàn)楸磉_(dá)式作為返回值要出現(xiàn)在函數(shù)的最后面
????????????return?index
????????}
????}
????s.len()
}

fn?main()?{
????println!(
????????"{}",?
????????first_word(&String::from("hello?world"))
????);?//?5
}

這段代碼首先使用 as_bytes 方法將 String 轉(zhuǎn)換為字節(jié)數(shù)組(u8),因?yàn)槲覀兊乃惴ㄐ枰来螜z查 String 中的字節(jié)是否為空格。

接著通過 iter 方法創(chuàng)建了一個(gè)可以遍歷字節(jié)數(shù)組的迭代器,我們會(huì)在后續(xù)詳細(xì)討論迭代器,目前只需要知道 iter 方法會(huì)依次返回集合中的每一個(gè)元素即可。

而隨后的 enumerate 則將 iter 的每個(gè)輸出逐一封裝在元組中返回,元組的第一個(gè)元素是索引,第二個(gè)元素是指向集合中字節(jié)的引用(&u8),使用 enumerate 可以較為方便地獲得迭代索引。

既然 enumerate 方法返回的是一個(gè)元組,那么我們就可以使用模式匹配來解構(gòu)它,就像 Rust 中其它使用元組的地方一樣。在 for 循環(huán)的遍歷語句中,我們指定了一個(gè)解構(gòu)模式,其中 i 是元組中的索引部分,而 &item 則稍微有點(diǎn)難理解。

首先迭代出的元組里面的第二個(gè)元素是 &u8,如果我們使用 item 遍歷,那么得到的 item 就是 &u8,在比較的時(shí)候還需要解引用,即 *item == b' '。而使用 &item 遍歷,那么 &item 得到的也是 &u8,顯然 item 就是 u8,我們就不需要解引用了。

在 for 循環(huán)的代碼塊中,使用了字面量語法來搜索數(shù)組中代表著空格的字節(jié),這段代碼會(huì)在搜索到空格時(shí)返回當(dāng)前的位置索引,并在搜索失敗時(shí)返回傳入字符串的長(zhǎng)度 s.len()。

現(xiàn)在我們初步實(shí)現(xiàn)了期望的功能,它能夠成功地搜索并返回字符串中第一個(gè)單詞結(jié)尾處的位置索引。但這里依然存在一個(gè)設(shè)計(jì)上的缺陷,我們將一個(gè) usize 值作為索引獨(dú)立地返回給調(diào)用者,而這個(gè)值在脫離了傳入的 &String 的上下文之后便毫無意義。

fn?first_word(s:?&String)?->?usize?{
????let?bytes?=?s.as_bytes();
????for?(index,?&item)?in?bytes.iter().enumerate()?{
????????if?item?==?b'?'?{
????????????return?index;
????????}
????}
????s.len()
}

fn?main()?{
????let?mut?s?=?String::from("hello?world");
????let?index?=?first_word(&s);
????println!("{}",?index);?//?5
????//?s.clear()?之后會(huì)清空字符串,將?s?變成?""
????s.clear();
????println!("s?=?{}",?s);??//?s?=
????//?s?被清空了,index?還是?5,但顯然此時(shí)?index?已經(jīng)沒有意義了
}

上面的程序在編譯器看來沒有任何問題,即便我們?cè)谡{(diào)用 s.clear() 之后使用 index 變量也是沒有問題的。同時(shí)由于 index 變量本身與 s 沒有任何關(guān)聯(lián),所以 index 的值始終都是5。但當(dāng)我們?cè)俅问褂?5 去從變量 s 中提取單詞時(shí),一個(gè) bug 就出現(xiàn)了:此時(shí) s 中的內(nèi)容在我們將 5 存入 index 之后發(fā)生了改變。

這種 API 的設(shè)計(jì)方式使我們需要隨時(shí)關(guān)注 word 的有效性,確保它與 s 中的數(shù)據(jù)是一致的,類似的工作往往相當(dāng)煩瑣且易于出錯(cuò)。這種情況對(duì)于另一個(gè)函數(shù) second_word 而言更加明顯,這個(gè)函數(shù)被設(shè)計(jì)來搜索字符串中的第二個(gè)單詞,它的簽名也許會(huì)被設(shè)計(jì)為下面這樣:

fn?second_word(s:?&String)?->?(usize,?usize)

現(xiàn)在我們需要同時(shí)維護(hù)起始和結(jié)束兩個(gè)位置的索引,這兩個(gè)值基于數(shù)據(jù)的某個(gè)特定狀態(tài)計(jì)算而來,但卻沒有跟數(shù)據(jù)產(chǎn)生任何程度上的聯(lián)系。于是我們有了 3 個(gè)彼此不相關(guān)的變量需要被同步,這可不妙。但幸運(yùn)的是,Rust 為這個(gè)問題提供了解決方案:字符串切片。

字符串切片

字符串切片是指 String 對(duì)象中某個(gè)連續(xù)部分的引用,它的使用方式如下所示:

fn?main()?{
????let?s?=?String::from("hello?world");
????let?s1?=?&s[0..5];
????let?s2?=?&s[6..11];
????println!("s1?=?{},?s2?=?{}",?s1,?s2);??
????//?s1?=?hello,?s2?=?world
}

這里的語法與創(chuàng)建指向整個(gè) String 對(duì)象的引用有些相似,但不同的是,新語法在結(jié)尾的地方多出了一段 [0..5]。這段額外的聲明告訴編譯器我們正在創(chuàng)建一個(gè) String 的切片引用,而不是對(duì)整個(gè)字符串本身的引用。

切片數(shù)據(jù)結(jié)構(gòu)在內(nèi)部存儲(chǔ)了指向起始位置的引用和一個(gè)描述切片長(zhǎng)度的字段,所以在上面的示例中,s2 是一個(gè)指向變量 s 第 7 個(gè)字節(jié)并且長(zhǎng)度為 5 的切片。

Rust的范圍語法 .. 有一個(gè)小小的語法糖:當(dāng)你希望范圍從第一個(gè)元素(也就是索引值為 0 的元素)開始時(shí),則可以省略兩個(gè)點(diǎn)號(hào)之前的值;同樣地,假如你的切片想要包含 String 中的最后一個(gè)字節(jié),你也可以省略雙點(diǎn)號(hào)之后的值;你甚至可以同時(shí)省略首尾的兩個(gè)值,來創(chuàng)建一個(gè)指向整個(gè)字符串所有字節(jié)的切片。

字符串切片的邊界必須位于有效的 UTF-8 字符邊界內(nèi),嘗試從一個(gè)多字節(jié)字符的中間位置創(chuàng)建字符串切片會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。但為了將問題簡(jiǎn)化,我們這里只使用 ASCII 字符集,至于 Unicode 后續(xù)討論。

基于所學(xué)到的這些知識(shí),讓我們開始重構(gòu) first_word 函數(shù)吧!該函數(shù)可以返回一個(gè)切片作為結(jié)果。另外,字符串切片的類型寫作 &str。

fn?first_word(s:?&String)?->?&str?{
????let?bytes?=?s.as_bytes();
????for?(index,?&item)?in?bytes.iter().enumerate()?{
????????if?item?==?b'?'?{
????????????return?&s[..?index];
????????}
????}
????&s[..]
}

fn?main()?{
????let?s?=?String::from("hello?world");
????println!("{}",?first_word(&s));??//?hello
}

調(diào)用新的 first_word 函數(shù)會(huì)返回一個(gè)與底層數(shù)據(jù)緊密聯(lián)系的切片作為結(jié)果,它由指向起始位置的引用和描述元素長(zhǎng)度的字段組成。當(dāng)然,我們也可以用同樣的方式重構(gòu) second_word 函數(shù)。

由于編譯器會(huì)確保指向 String 的引用持續(xù)有效,所以我們新設(shè)計(jì)的接口變得更加健壯且直觀了。還記得之前故意構(gòu)造出的錯(cuò)誤嗎?那段代碼在搜索完成并保存索引后清空了字符串的內(nèi)容,這使得我們存儲(chǔ)的索引不再有效。因此它在邏輯上明顯是有問題的,卻不會(huì)觸發(fā)任何編譯錯(cuò)誤,這個(gè)問題只會(huì)在使用第一個(gè)單詞的索引去讀取空字符串時(shí)暴露出來,而切片的引入使我們可以在開發(fā)早期快速地發(fā)現(xiàn)此類錯(cuò)誤。

fn?first_word(s:?&String)?->?&str?{
????let?bytes?=?s.as_bytes();
????for?(index,?&item)?in?bytes.iter().enumerate()?{
????????if?item?==?b'?'?{
????????????return?&s[..?index];
????????}
????}
????&s[..]
}

fn?main()?{
????let?mut?s?=?String::from("hello?world");
????let?word?=?first_word(&s);
????s.clear();
????println!("{}",?word);?
}

上述代碼執(zhí)行會(huì)報(bào)錯(cuò):

錯(cuò)誤很明顯,s 已經(jīng)作為不可變引用被借用了,因此不能再作為可變引用被借用。

那么問題來了,s 作為不可變引用借給誰了呢?顯然是 word,因?yàn)樗亲址衅?,是指向字符串的不可變引用;然后又是誰想要借 s 的可變引用呢?顯然是 s.clear(),由于 clear 需要截?cái)喈?dāng)前的 String 實(shí)例,所以調(diào)用 clear 需要傳入一個(gè)可變引用。

因此最終編譯失敗,所以 Rust 不僅使我們的 API 更加易用,它還在編譯過程中幫助我們避免了此類錯(cuò)誤。

字符串字面量就是切片

還記得我們講的字符串字面量嗎?它是直接存儲(chǔ)在了二進(jìn)制程序中。在學(xué)習(xí)了切片之后,我們現(xiàn)在可以更恰當(dāng)?shù)乩斫庾址置媪苛恕?/p>

let?s?=?"hello?world";

在這里,變量 s 的類型其實(shí)就是 &str:它是一個(gè)指向二進(jìn)制程序特定位置的切片。正是由于&str是一個(gè)不可變的引用,所以字符串字面量是不可變的。

字符串切片作為參數(shù)

既然我們可以分別創(chuàng)建字符串字面量和 String 的切片,那么就能夠進(jìn)一步優(yōu)化 first_word 函數(shù)的接口,下面是它目前的簽名:

fn?first_word(s:?&String)?->?&str

比較有經(jīng)驗(yàn)的 Rust 開發(fā)者往往會(huì)采用下面的寫法,這種改進(jìn)后的簽名使得函數(shù)可以同時(shí)處理 &String 與 &str:

fn?first_word(s:?&str)?->?&str

總結(jié):當(dāng)函數(shù)參數(shù)類型為 &String,那么只能傳 String 的引用,不可以傳切片;如果參數(shù)類型為 &str,那么既可以傳 String 的引用,也可以傳切片。說白了,在 String 類型的值前面加上一個(gè) & 就表示 String 的引用(&String),而在引用的基礎(chǔ)之上,在后面再加上 [..],那么就表示字符串切片(&str)。

let?s1?=?String::from("hello?world");
//?合法,&str?支持字符串引用
let?s2:?&str?=?&s1;??
//?合法,&str?支持字符串切片,因?yàn)楸旧砭褪亲址衅愋?
let?s2:?&str?=?&s1[..];??
//?合法,字符串字面量本身就是一個(gè)不可變的字符串切片
let?s2:?&str?=?"hello?world";??
//?以上三者等價(jià),因?yàn)?&str?既可以接收?&String,也可以接收?&str

let?s3:?&String?=?&s1;??//?合法
let?s3:?&String?=?&s1[..];??//?不合法
let?s3:?&String?=?"hello?world";??//?不合法
//?因?yàn)?&String?只能接收?&String,不能接收?&str

//?最后,字符串切片雖然能接收?String?的引用,但?String?是無法接收的
//?不合法,&str?只能接收?&str、&String,無法接收?String
let?s2:?&str?=?s1;??

因此我們?cè)谠O(shè)計(jì)函數(shù)時(shí),使用字符串切片來代替字符串引用會(huì)使我們的 API 更加通用,且不會(huì)損失任何功能。

其它類型的切片

從名字上就可以看出來,字符串切片是專門用于字符串的。但實(shí)際上,Rust 還有其他更加通用的切片類型,以下面的數(shù)組為例:

let?a?=?[1,?2,?3,?4,?5];?

就像我們想要引用字符串的某個(gè)部分一樣,你也可能會(huì)希望引用數(shù)組的某個(gè)部分。這時(shí),我們可以這樣做:

let?a?=?[1,?2,?3,?4,?5];
let?slice?=?&a[1..3];

這里的切片類型是 &[i32],它在內(nèi)部存儲(chǔ)了一個(gè)指向起始元素的引用及長(zhǎng)度,這與字符串切片的工作機(jī)制完全一樣。并且我們將在各種各樣的集合中接觸到此類切片,而在后續(xù)討論動(dòng)態(tài)數(shù)組時(shí)再來介紹那些常用的集合。

以上就是深入了解Rust的切片使用的詳細(xì)內(nèi)容,更多關(guān)于Rust切片的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 如何基于Rust實(shí)現(xiàn)文本搜索minigrep

    如何基于Rust實(shí)現(xiàn)文本搜索minigrep

    這篇文章主要介紹了基于Rust實(shí)現(xiàn)的文本搜索minigrep,本次演示介紹針對(duì)原作者代碼程序的查詢邏輯做了一點(diǎn)點(diǎn)小的優(yōu)化,原程序邏輯的查詢是放在了程序運(yùn)行的時(shí)候,邏輯修改后啟動(dòng)的時(shí)候可以添加參數(shù),也可以啟動(dòng)后添加,需要的朋友可以參考下
    2024-08-08
  • Rust?Postgres實(shí)例代碼

    Rust?Postgres實(shí)例代碼

    Rust Postgres是一個(gè)純Rust實(shí)現(xiàn)的PostgreSQL客戶端庫,本文主要介紹了Rust?Postgres實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-05-05
  • Rust版本號(hào)的使用方法詳解

    Rust版本號(hào)的使用方法詳解

    在 Rust 項(xiàng)目中,版本號(hào)的使用遵循語義版本控制(Semantic Versioning)原則,確保版本號(hào)的變化能準(zhǔn)確反映代碼的變更情況,本文給大家詳細(xì)解釋了Rust版本號(hào)用法,需要的朋友可以參考下
    2024-01-01
  • Rust如何進(jìn)行模塊化開發(fā)技巧分享

    Rust如何進(jìn)行模塊化開發(fā)技巧分享

    Rust模塊化,模塊化有助于代碼的管理和層次邏輯的清晰,本文主要介紹了Rust如何進(jìn)行模塊化開發(fā),結(jié)合實(shí)例代碼給大家講解的非常詳細(xì),需要的朋友可以參考下
    2023-01-01
  • Rust 模式匹配示例詳解

    Rust 模式匹配示例詳解

    這篇文章主要為大家介紹了Rust 模式匹配示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • rust 一個(gè)日志緩存記錄的通用實(shí)現(xiàn)方法

    rust 一個(gè)日志緩存記錄的通用實(shí)現(xiàn)方法

    本文給出了一個(gè)通用的設(shè)計(jì)模式,通過建造者模式實(shí)例化記錄對(duì)象,可自定義格式化器將實(shí)例化后的記錄對(duì)象寫入到指定的緩存對(duì)象中,這篇文章主要介紹了rust 一個(gè)日志緩存記錄的通用實(shí)現(xiàn)方法,需要的朋友可以參考下
    2024-04-04
  • 使用Rust制作康威生命游戲的實(shí)現(xiàn)代碼

    使用Rust制作康威生命游戲的實(shí)現(xiàn)代碼

    這篇文章主要介紹了使用Rust制作康威生命游戲,初始rust項(xiàng)目,使用wasm的項(xiàng)目模板,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-09-09
  • C和Java沒那么香了,Serverless時(shí)代Rust即將稱王?

    C和Java沒那么香了,Serverless時(shí)代Rust即將稱王?

    Serverless Computing,即”無服務(wù)器計(jì)算”,其實(shí)這一概念在剛剛提出的時(shí)候并沒有獲得太多的關(guān)注,直到2014年AWS Lambda這一里程碑式的產(chǎn)品出現(xiàn)。Serverless算是正式走進(jìn)了云計(jì)算的舞臺(tái)
    2021-06-06
  • 關(guān)于rust的模塊引入問題

    關(guān)于rust的模塊引入問題

    Rust 語言是一種高效、可靠的通用高級(jí)語言,它的執(zhí)行效率也是令人稱贊的,是一種少有的兼顧開發(fā)效率和執(zhí)行效率的語言,這篇文章主要介紹了rust的模塊引入相關(guān)知識(shí),需要的朋友可以參考下
    2022-10-10
  • 詳解Rust中的方法

    詳解Rust中的方法

    方法其實(shí)就是結(jié)構(gòu)體的成員函數(shù),在C語言中的結(jié)構(gòu)體是沒有成員函數(shù)的,但是Rust畢竟也是一門面向?qū)ο蟮木幊陶Z言,所以給結(jié)構(gòu)體加上方法的特性很符合面向?qū)ο蟮奶攸c(diǎn),這篇文章主要介紹了Rust中的方法,需要的朋友可以參考下
    2022-10-10

最新評(píng)論