<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          懂你,更懂Rust系列之ownership

          共 6373字,需瀏覽 13分鐘

           ·

          2021-04-24 23:28

          我祈禱擁有一顆透明的心靈

          和會(huì)流淚的眼睛

          給我再去相信的勇氣

          oh~越過(guò)謊言去擁抱你

          每當(dāng)我找不到存在的意義

          每當(dāng)我迷失在黑夜里

          oh~夜空中最亮的星

          請(qǐng)指引我靠近你

          夜空中最亮的星



          在了解這個(gè)ownership之前,先需要認(rèn)識(shí)下兩種計(jì)算機(jī)內(nèi)存結(jié)構(gòu)

          Heap《堆》和Stack《?!?br />


          Heap和Stack都是內(nèi)存的一部分,在程序運(yùn)行的時(shí)候,Heap和Stack中保存有系統(tǒng)運(yùn)行的相關(guān)數(shù)據(jù)或變量,Heap和Stack的結(jié)構(gòu)方式不同,其中Stack是一個(gè)先進(jìn)后出的隊(duì)列結(jié)構(gòu),如下圖:


          2459684929acf3ee20befa27e2ce44a4.webp


          如上圖所示,Stack中保存了abcde五個(gè)數(shù)據(jù),其中按照abcde的順序保存,獲取數(shù)據(jù)的時(shí)候,只能按照edcba的順序出棧。


          具體可以參照有一篇關(guān)于JVM虛擬機(jī)描述的Java虛擬機(jī)棧中操作數(shù)棧的描述的那樣


          淺談Java虛擬機(jī)運(yùn)行機(jī)制(一)


          在Rust語(yǔ)言中,所有存儲(chǔ)在Stack上的數(shù)據(jù)必須有一個(gè)已知的固定的大小,Rust編譯的時(shí)候,那些未知大小的數(shù)據(jù),或者大小可能發(fā)生改變的數(shù)據(jù),將會(huì)被保存在Heap中,Heap的組織性較差,當(dāng)保存數(shù)據(jù)在Heap中的時(shí)候,需要確定的空間大小,內(nèi)存分配器《memory allocator》在Heap中會(huì)找到一個(gè)足夠大小的空間存儲(chǔ)數(shù)據(jù),同時(shí)返回一個(gè)指針,即表明該數(shù)據(jù)所在Heap中的位置。這個(gè)過(guò)程即為堆上分配。



          這里可以看出,在Stack中分配數(shù)據(jù)的效率遠(yuǎn)遠(yuǎn)比在Heap中分配數(shù)據(jù)的效率要高很多,因?yàn)镾tack中,只需要將數(shù)據(jù)壓入棧頂即可,而Heap中則需要去尋找一個(gè)足夠大的地方,同時(shí)獲取這個(gè)地方的指針?biāo)饕?,同樣的,獲取數(shù)據(jù),Stack只需要彈出棧頂元素即可,而Heap中則需要根據(jù)索引指針找到數(shù)據(jù)所在位置,然后才能獲取數(shù)據(jù)。


          通常情況下,當(dāng)代碼調(diào)用一個(gè)函數(shù)的時(shí)候,傳遞到函數(shù)中的值(可能包含指向Heap中的指針對(duì)象)和函數(shù)局部變量將被壓入Stack中,當(dāng)函數(shù)調(diào)用結(jié)束,這些值將會(huì)被彈出Stack



          ownership則是解決如何跟蹤代碼中哪部分使用了Heap中的數(shù)據(jù),最小化堆中數(shù)據(jù)重復(fù)量,以及及時(shí)清理未使用的堆中數(shù)據(jù)。


          所有權(quán)規(guī)則<ownership Rule> :


          Each value in Rust has a variable that’s called its owner.

          There can only be one owner at a time.

          When the owner goes out of scope, the value will be dropped.


          大致意思就是:


          Rust語(yǔ)言中每一個(gè)變量值都有一個(gè)屬于自己的所有權(quán)

          一次只能擁有一個(gè)所有權(quán)

          當(dāng)所有權(quán)超出作用域時(shí),該值將被刪除


          一、Variable Scope


          19b765e236feffc3a67fda635a59d044.webp


          看上面這段代碼,在main方法里申明了一個(gè)變量s,然后打印輸出,在s還沒(méi)有被申明的時(shí)候,s是無(wú)效的,當(dāng)退出main方法的時(shí)候,s離開作用域,同時(shí)也變無(wú)效


          即:

          當(dāng)s進(jìn)入作用域時(shí),它是有效的,

          然后它一直有效,直到超出作用域

          這里需要指出的是,在前面說(shuō)到的Rust語(yǔ)言基本類型<懂你,更懂Rust系列之?dāng)?shù)據(jù)類型>(除開數(shù)組和元祖)的數(shù)據(jù),都是存儲(chǔ)在Stack中的,并且在它們的作用域結(jié)束時(shí),彈出Stack。



          所以,基于基本類型保存在Stack中的數(shù)據(jù),不需要過(guò)多的去探討,重點(diǎn)需要關(guān)注的是那些保存在Heap中的數(shù)據(jù),上面這個(gè)小例子,過(guò)于簡(jiǎn)化,因?yàn)檫@里申明了一個(gè)定長(zhǎng)的string類型的數(shù)據(jù),即這里是將string類型的s硬編碼到程序中,所以其執(zhí)行效率是很高的。那么對(duì)于那些未知大小的數(shù)據(jù)情況呢?



          比如下面代碼:


          d96ee74307dbe1079b46f76423bbe5fa.webp


          這里的雙冒號(hào)(::)是一個(gè)操作符號(hào),String::from是指從字符串文本中創(chuàng)建字符串,s.push_str是指將變量s追加字符串

          這里可以看到,這是一個(gè)可變、不定長(zhǎng)的字符串類型,不能通過(guò)硬編碼的方式在程序中引入二進(jìn)制的內(nèi)存塊來(lái)保存數(shù)據(jù)了。


          所以,對(duì)于這種類型的string,需要在Heap中分配一定數(shù)量的內(nèi)存,同時(shí),這需要程序或者程序員做以下事:


          1、必須在程序運(yùn)行的時(shí)候,從內(nèi)存分配器中請(qǐng)求內(nèi)存

          2、當(dāng)用完這個(gè)字符串的時(shí)候,把這個(gè)內(nèi)存返回給分配器


          第一件事,在調(diào)用String::from的時(shí)候,程序給我們完成了,這個(gè)在很多編程語(yǔ)言中都是比較相似的,

          第二件事,在不同的編程語(yǔ)言中,是有很大區(qū)別的,在有GC收集器的語(yǔ)言(比如Java、Python等),GC收集器會(huì)跟蹤并識(shí)別這些區(qū)域,然后將其清理掉。在沒(méi)有GC收集器時(shí),需要我們識(shí)別出內(nèi)存什么時(shí)候不再被使用,并顯示調(diào)用代碼,返回內(nèi)存


          做到第二點(diǎn)其實(shí)是比較難的,如果忘記了,則浪費(fèi)內(nèi)存,容易引起內(nèi)存泄漏,如果太早的調(diào)用代碼清除返回內(nèi)存,則可能使定義的變量是個(gè)無(wú)效的變量。如果做了2次,那也是個(gè)BUG,我們就必須將分配的一個(gè)內(nèi)存和釋放的一個(gè)內(nèi)存精確的配對(duì)。



          Rust語(yǔ)言提供了不同的路徑解決這個(gè)內(nèi)存回收的問(wèn)題,一旦擁有內(nèi)存的變量超出作用域,則內(nèi)存就自動(dòng)釋放并返回。


          針對(duì)上面的代碼做些說(shuō)明:


          e6f351b96686bfac0748d232556435e0.webp


          類似在C++語(yǔ)言中,在項(xiàng)目生命周期結(jié)束時(shí),重新分配資源的模式被稱為RAII,這里可以類比的理解


          ?二、Ways Variables and Data Interact: Move


          看下面代碼:


          383e75ead4eca476378a3f04d16dd391.webp


          這里不難理解,首先定義一個(gè)變量x,將5賦值給這個(gè)變量,然后再次復(fù)制一個(gè)x的值5綁定到y(tǒng)中,此時(shí)我們擁有了2個(gè)變量值,都等于5,此時(shí)兩個(gè)5均被壓入Stack中:


          b68de137bf4a78e0f167309a971ca60d.webp


          因?yàn)榛绢愋投际嵌ㄩL(zhǎng)大小的,是保存在Stack中的,那么如果是Heap中的String呢?


          17d6a0455394271838f258d11c5b639d.webp


          這里和上一段代碼是很類似的,但是其內(nèi)存實(shí)現(xiàn)方式是有很大區(qū)別的:


          首先我們來(lái)看下s1在內(nèi)存中的保存方式:


          6574ae169b9b0d962887de115ebbbfc0.webp

          首先,字符串類型由3部分組成,如上圖左邊所示:指向保存字符串內(nèi)容的內(nèi)存指針地址、字符串長(zhǎng)度、字符串容量,這些數(shù)據(jù)是保存在Stack中的,而這個(gè)內(nèi)存指針指向堆中字符串內(nèi)容。


          那么當(dāng)執(zhí)行l(wèi)et s2 = s1的時(shí)候,發(fā)生了什么呢?


          e059fbdf9cd8566bb32904faceac105b.webp


          如上圖所示,當(dāng)執(zhí)行l(wèi)et s2 = s1時(shí),程序會(huì)復(fù)制Stack中的內(nèi)容,即s2的內(nèi)存地址指針指向了原本s1指向的地址,Heap中的內(nèi)容并沒(méi)有跟隨堆的復(fù)制而復(fù)制。


          前面我們說(shuō)過(guò),當(dāng)變量超出作用域時(shí),會(huì)自動(dòng)釋放內(nèi)存(rust調(diào)用drop實(shí)現(xiàn)),但這里,兩個(gè)變量同時(shí)指向了相同的堆內(nèi)存地址,即當(dāng)s1和s2超出作用域的時(shí)候,將釋放2次相同的內(nèi)存,這就是double free error,屬于一個(gè)內(nèi)存安全漏洞,兩次釋放可能導(dǎo)致內(nèi)存損壞。


          為了解決這個(gè)問(wèn)題,Rust認(rèn)為,當(dāng)s2創(chuàng)建完成后,s1不再有效。即:


          ffd950a690e578855317d20c1ede3ee8.webp


          比如代碼嘗試使用s1:


          c14d862f5b92bdcf93129fdd35b1ad8f.webp


          編譯報(bào)錯(cuò):


          acda8853e9602c6152c7b2c52fda28c5.webp


          這里既是數(shù)據(jù)之間的移動(dòng),即移動(dòng)之后,原本的對(duì)象無(wú)效了。


          這種移動(dòng)僅僅限于在堆上分配內(nèi)存保存的數(shù)據(jù),基本類型由于是在棧上存儲(chǔ),則是可以的,比如前面的代碼是可以運(yùn)行的:


          98348566c68e8d46dde55bf2ee01fba6.webp


          編譯運(yùn)行:


          347dc6ec43c1f4352c086c6424243454.webp


          ?三、Ways Variables and Data Interact: Clone


          那么針對(duì)上面移動(dòng)的字符串例子,如果我們確實(shí)需要復(fù)制一個(gè)呢?這里有一種克隆方法:


          229fdea79dd79c72da956232e90a0e2d.webp


          編譯運(yùn)行:


          886ab39f0c1f6f8d0d421269c70a56ac.webp


          此時(shí)內(nèi)存發(fā)生的變化則是這樣了:


          f59307200ae98556a743b0fb85670ddb.webp


          即Heap中的內(nèi)存也得到了復(fù)制,不過(guò)這是一個(gè)很昂貴的代價(jià),系統(tǒng)需要花費(fèi)比較大的消耗去完成這件事。


          克隆結(jié)果的兩個(gè)對(duì)象,其==比較結(jié)果是為true的


          f63aeca5c012b529d1f602b428b4cd7a.webp


          編譯運(yùn)行:


          ee05f7361176d4e1ec195d557dc8d208.webp


          四、Stack-Only Data: Copy


          這里就是針對(duì)前面介紹的基本類型的棧上分配數(shù)據(jù)是不會(huì)產(chǎn)生移動(dòng)的,不在過(guò)多贅述:


          69671817e66ebb5f4d1ef9aef1cf43e7.webp


          編譯運(yùn)行:


          6cbd337580124e46dd2e5c6fa5ce8f2f.webp


          五、Ownership and Functions



          看如下代碼:


          61a2c85a27f3c44770b4a77cb6fd7803.webp


          需要指出的是,當(dāng)非基本類型變量進(jìn)入函數(shù)體時(shí),變量產(chǎn)生移動(dòng),原始變量不在有效。這是區(qū)別于其他很多高級(jí)語(yǔ)言的。


          編譯運(yùn)行:


          40d9850b6254459621e14ba99be3e405.webp


          但是如果我們?cè)噲D在main函數(shù)內(nèi),takes_ownership函數(shù)之后,調(diào)用s,編譯就會(huì)報(bào)錯(cuò),比如:



          2945e8e1847b57d01b802812d0cda875.webp


          編譯報(bào)錯(cuò):


          47b751bcc36d3ef6604f050d30c8cf66.webp




          六、Return Values and Scope


          函數(shù)返回值,也是具有移動(dòng)的,看下面代碼:


          1b1fee6fc531b2e9ad0c1e7f6db5ad8b.webp


          擁有返回值的函數(shù),在被調(diào)用之后,其返回值將移動(dòng)到調(diào)用者


          所以,在調(diào)用takes_and_gives_back函數(shù)之后,試圖拿到s2也是會(huì)編譯報(bào)錯(cuò)的,這里就不做試驗(yàn)了。


          可以簡(jiǎn)單做個(gè)小結(jié):


          變量的所有權(quán),在每次給另外一個(gè)變量賦值時(shí),將會(huì)移動(dòng)該變量,當(dāng)變量超出作用域時(shí),變量被回收,釋放內(nèi)存。



          函數(shù)會(huì)獲取參數(shù)的所有權(quán),被調(diào)用者調(diào)用,又將返回所有權(quán)給調(diào)用者,那么有時(shí)候,需要讓函數(shù)只是使用某個(gè)值,并不讓其產(chǎn)生變量移動(dòng)效果,怎么解決這個(gè)問(wèn)題呢?一般來(lái)說(shuō),可以使用函數(shù)返回值是元組的方式來(lái)完成這個(gè)操作。


          即:


          3946cb223ddcf465a147e86cc416d6a4.webp


          編譯運(yùn)行:


          b7d12823c9dacb9e5323d8b8deea019f.webp


          這里,length_string函數(shù)返回了元祖數(shù)據(jù),將參數(shù)返回了,還可以繼續(xù)用。


          這里的代碼稍微做改變:


          f3bd1ee818fe60555e64aaff371d6204.webp


          這里是會(huì)編譯報(bào)錯(cuò)的,


          當(dāng)執(zhí)行返回值構(gòu)造方法后,其第一個(gè)元素為_string,所以參數(shù)_string已經(jīng)發(fā)生了移動(dòng),不在有效,當(dāng)調(diào)用_string.len()方法時(shí),_string已經(jīng)無(wú)效了。


          編譯報(bào)錯(cuò)


          2c159c226dfa9b100d48b6499f83a420.webp



          如果在實(shí)際項(xiàng)目中,需要調(diào)用函數(shù)的時(shí)候,還可以返回參數(shù),通過(guò)這種方式固然可以實(shí)現(xiàn),但是有點(diǎn)太過(guò)于麻煩了,這時(shí)候,Rust提出了另外一個(gè)概念,引用-->References


          七、References?&?Borrowing


          這里還是為了解決上面那個(gè)問(wèn)題,看下面代碼:


          4745aee1e2ee9e1bf8857c84e3513550.webp


          編譯運(yùn)行:


          f1eff64a6bee65e6bfb6b7739bb6dd4f.webp

          這里可以看到,s作為參數(shù)傳入函數(shù)length_string中后,并沒(méi)有發(fā)生移動(dòng)。在函數(shù)length_string中,_string變量其實(shí)只是引用了s,并沒(méi)有將s的所有權(quán)移動(dòng)到_string中,所以當(dāng)_string超出作用域后,其指向的值,并不會(huì)被刪除。


          其內(nèi)存指針如下圖:


          505292a5d52be7e645f0fa6f1ba1349a.webp


          給上面代碼加些注釋:


          be53d21d0f2e8bb4eb463b98ced62c42.webp



          我們把&s稱為s的引用<references>
          把這種引用稱為函數(shù)的參數(shù)租借<borrowing>


          一般來(lái)說(shuō),在調(diào)用函數(shù)的時(shí)候,需要進(jìn)行參數(shù)值的一些改變,這里由于沒(méi)有得到參數(shù)所有權(quán),所以是會(huì)編譯報(bào)錯(cuò)的


          這里舉個(gè)簡(jiǎn)單的例子,引用好比現(xiàn)在去租了一個(gè)房子,但是并沒(méi)有擁有房子的所有權(quán),就只能簡(jiǎn)單的住在這里,所以,如果在沒(méi)有經(jīng)過(guò)房東的同意,假設(shè)我修改了房子的結(jié)構(gòu),那肯定是會(huì)發(fā)生狀況的。


          比如:


          60adda5390b734f346c4e30e4bce64a3.webp


          這里在調(diào)用函數(shù)的時(shí)候,對(duì)參數(shù)進(jìn)行了拼接,編譯報(bào)錯(cuò):


          1e9cfed3ab4a141620b0588bb7e75771.webp




          `_string` is a `&` reference, so the data it refers to cannot be borrowed as mutable

          這個(gè)報(bào)錯(cuò)信息給與了我們一個(gè)解決方案:


          即,它告訴我們,這個(gè)引用必須是可變的




          于是,改進(jìn)代碼:


          b75596f327e085a2f9ed17563cf996d6.webp


          編譯運(yùn)行:


          ad2256ff639093eddbe1bc295edfaa44.webp


          這里有一個(gè)需要記住的地方,可變引用在特定范圍內(nèi),只能有一個(gè)對(duì)特定數(shù)據(jù)塊的可變引用,比如下面代碼:


          95e4de483a6da032796d12d2be71b715.webp


          編譯報(bào)錯(cuò):


          345a1ea134fa749037939eef551b3fec.webp


          移動(dòng)下位置:


          67e5319582d6d23f01b124be35292543.webp


          這個(gè)時(shí)候,編譯運(yùn)行:



          b8804c103549d4bc2e960c787bcede62.webp


          這個(gè)時(shí)候?yàn)槭裁淳褪强梢缘??可以想一下,看看前面的?nèi)容是不是忘了?


          tips:_s1變量在調(diào)用length_string函數(shù)的時(shí)候,傳入的是_s1,_s1發(fā)生了移動(dòng),_s1不在有效。


          在特定范圍內(nèi),保證只有一個(gè)可變引用,可以防止多個(gè)指針指向相同對(duì)象時(shí),發(fā)生對(duì)指針指向?qū)ο蟾淖冎邓鶎?dǎo)致的數(shù)據(jù)競(jìng)爭(zhēng),這種情況在其他多數(shù)語(yǔ)言中都可能發(fā)生,但Rust在編譯階段就防止這種情況。


          看下面這種情況:


          4a77bf5ef43c8729dca276815d4b25b7.webp


          這里12行,定義_s3為可變變量s的不可變引用,_s4為可變變量s的不可變引用,這兩個(gè)變量的定義,都沒(méi)有問(wèn)題,只是后面定義了一個(gè)_s5為可變變量s的可變引用,


          編譯報(bào)錯(cuò):


          c54fc36ccce6603af39a2712a76cc16f.webp


          報(bào)錯(cuò)信息告訴我們,當(dāng)變量不可變引用存在租借的情況,在變量不可變引用沒(méi)有超過(guò)作用域的時(shí)候不能定義變量為可變引用。


          修改下代碼:


          93f0b3065d39ff0b85f2dbd8d0ac6931.webp


          編譯運(yùn)行:


          8b49f36044ed0c03256209381e5738ad.webp


          因?yàn)楫?dāng)調(diào)用println方法后,_s3和_s4發(fā)生了移動(dòng),不在有效,所以可以再次定義變量可變引用。



          通常情況下,在有指針存在的語(yǔ)言中,可能出現(xiàn)指針存在,但是指針指向的內(nèi)存已經(jīng)被釋放的情況,即dangling pointer,在Rust中,這種情況,編譯階段就會(huì)出現(xiàn)錯(cuò)誤:



          比如:


          d7f73d11035eb2b8acc1184812664ba0.webp


          這里,定義一個(gè)String類型,然后返回這個(gè)變量_k的引用,當(dāng)_k超出dangling_pointer函數(shù)的時(shí)候,程序會(huì)調(diào)用drop,清理掉_k的內(nèi)存,但是返回了_k的棧引用,

          這其實(shí)是個(gè)錯(cuò)誤示范


          編譯報(bào)錯(cuò):


          6a87ac96f20f543dd8bd0bce865b100c.webp


          但是可以這樣:


          43b3a6ffcbe5f92e8b2e6794cf160e54.webp


          編譯運(yùn)行


          87494d7b6f4b7224e5039c7d1393c51d.webp



          八、The Slice Type


          下面實(shí)現(xiàn)這么一個(gè)功能,從字符串中找到某個(gè)字符在字符串中所在的位置,


          b2ae1c807ee7874c01132b38481a14a9.webp


          程序編譯運(yùn)行肯定是沒(méi)有什么問(wèn)題的,

          也可以很容易的拿到_p的值是5


          dda211a67befe3cdf18644e07f3da9fa.webp


          那么,接下來(lái),拿到這個(gè)5,一般來(lái)說(shuō),是需要有所作用的,比如根據(jù)這個(gè)5,獲取_s中指定部分?jǐn)?shù)據(jù),就上面代碼示例來(lái)看,也是可以調(diào)用截取,根據(jù)_s和_p做一些操作,那如果,我們清空_s呢?


          比如:


          51daa226ed41e121423245fe1b7cd981.webp



          在調(diào)用_s.clear(),之后,仿佛這個(gè)_p的意義就不大了<這里僅僅是為了接下來(lái)的內(nèi)容引入,不接受類似‘不調(diào)用_s.clear()就好了’之類的反駁。(#^.^#)>,因?yàn)闆](méi)有辦法截取了,_s已經(jīng)空了!


          Rust語(yǔ)言提供了一種類型,幫助我們解決這個(gè)問(wèn)題,即,可以獲取字符串指定部分內(nèi)容,同時(shí)將截取的部分內(nèi)容稱為:片(slice)


          看下面代碼:


          2d8dea08780037df2219bd2f203631a2.webp


          編譯輸出:


          b07735a4fa3217d13d88672e5631af08.webp


          這里就是簡(jiǎn)單的slice類型的定義,


          細(xì)心一點(diǎn),可以發(fā)現(xiàn)一個(gè)問(wèn)題:這里定義的時(shí)候,用的&s,即使用了s的引用,那么可以直接使用s的截取部分么?即:


          de170ffa0e372c1bcdb2a99da7a6c491.webp



          這里編譯是會(huì)報(bào)錯(cuò)的:


          d5060b583153c2ef9d83554698edaadc.webp



          編譯器說(shuō),在編譯階段,無(wú)法知道str類型的固定的長(zhǎng)度


          這里仿佛又獲得一個(gè)新的東西,str


          在Rust中,String類型具體分為兩種,一個(gè)是未知長(zhǎng)度的字符串類型String,一種是已知長(zhǎng)度大小的str


          已知長(zhǎng)度大小的字符串,其內(nèi)存分配的時(shí)候,是在棧中保存的,所以不會(huì)存在移動(dòng),比如下面代碼:


          50e866b87848ec165186b7d72f9f1072.webp


          這里如果_是String類型的,在調(diào)用look_for_str函數(shù)之后,肯定會(huì)發(fā)生移動(dòng),所以,后面打印輸出的時(shí)候,是一定會(huì)報(bào)錯(cuò)的,但是這里由于是棧中內(nèi)存,可以視為和Rust基本類型一樣,不會(huì)發(fā)生移動(dòng),而打印輸出的時(shí),并沒(méi)有超出_s的作用域,所以,是沒(méi)有問(wèn)題的


          編譯運(yùn)行:


          de740a4c0ca82f036b3e2ab810d14e86.webp


          而這樣是肯定會(huì)發(fā)生錯(cuò)誤的:


          1443608151399a7a0fb43d80b0297f7e.webp


          編譯報(bào)錯(cuò):


          aa415a6a9688f0c2b97d59c8d659002a.webp


          所以,這里就比較容易的理解,剛剛定義slice類型時(shí),為啥要使用引用?


          因?yàn)槿绻皇褂靡?,那么定義的string類型是str的,str是需要固定大小的,然而本身_s并不是str,而是string類型,所以截取出來(lái)的_s部分也是一個(gè)string引用,并不是str


          那么接下來(lái),繼續(xù)前面的內(nèi)容,slice定義語(yǔ)法是

          [starting_index..ending_index]


          這里的starting_index是起始下標(biāo),

          ending_index是結(jié)束下標(biāo),

          兩者是可以省略的,


          [..ending_index]

          等價(jià)于[0..ending_index]


          [starting_index..]

          等價(jià)于[starting_index..string.len()]


          [..]

          等價(jià)于[0..string.len()]

          定義的slice是變量的引用對(duì)象的部分指向,內(nèi)存并沒(méi)有生成多余的空間來(lái)保存這個(gè)數(shù)據(jù),比如前面的定義代碼:


          84886c2a23615da10fcbb664d55f4e9d.webp


          內(nèi)存結(jié)構(gòu)為:


          8a59d05c4b2e8a784b72a70c8b466652.webp


          注意,這里的索引截取,必須位于有效的UTF-8字符邊界內(nèi),如果是從一個(gè)多字節(jié)字符的中間位置截取,這里是不允許的,比如:


          1b980c8cbe33260faa850e6fdcad0fba.webp


          編譯報(bào)錯(cuò):


          d3dc08101a160a28d4140518f79fc255.webp

          這個(gè)時(shí)候,只能這樣:


          939aba2914c16aa8da68929df710d722.webp


          編譯運(yùn)行:


          d64427dd6cc7432693e88971ea418b71.webp


          在有了這些基本語(yǔ)法之后,再來(lái)看看前面那個(gè)截取字符串的功能:


          4f761b46f7b9d943b48286832dfdf460.webp


          這里合理的截取到了需要的部分:


          65cc226432b0c39dc7f153e6def2d982.webp


          此時(shí),如果在調(diào)用打印輸出前,將_s清空,則編譯報(bào)錯(cuò),此時(shí),可以有效的幫助我們發(fā)現(xiàn),如果這樣做,那么_p將變得毫無(wú)意義:


          即:


          007eb2187723126f0699bc13dfae8807.webp


          編譯報(bào)錯(cuò):


          6272c43d54b01f1ce1a656f7b706a4d6.webp


          此外,還可以將slice當(dāng)做參數(shù)傳入函數(shù)中:


          591ed38dbf153271bbc097a6484582e7.webp


          編譯運(yùn)行:


          7848960aa2aa2b642f32cafc571d43b6.webp



          關(guān)于Rust語(yǔ)言的一些特性,就先到這兒了,主要需要多多自己寫代碼,然后去理解下。后續(xù)將分享實(shí)體結(jié)構(gòu)相關(guān)



          喜歡的歡迎關(guān)注轉(zhuǎn)發(fā),謝謝



          點(diǎn)個(gè)再看和贊咯,(#^.^#)

          瀏覽 99
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色先锋资源一区二区 | 国产三级片网站在线观看 | 欧美三级在线观看视频 | 艹逼美女a级毛片 | 水多多成人视频 |