rust实现wss访问_Rust的所有权,第2部分
仍然沒有問題。
上次查看Rust的所有權(quán)時(shí),我們查看了Rust如何使用范圍來確定何時(shí)應(yīng)該刪除或釋放內(nèi)存中的資源/數(shù)據(jù)。
我們看到,對(duì)于具有"復(fù)制特征"的類型(即,其數(shù)據(jù)可以存儲(chǔ)在堆棧中的類型),所有權(quán)模型的行為類似于可能使用不同范式的其他語言,例如垃圾回收。 但是對(duì)于沒有這種特征的類型,我們需要更加了解所有權(quán)規(guī)則。
盡管所有權(quán)可能會(huì)帶來一些設(shè)計(jì)折衷,但它會(huì)以靈活性,明確性和安全性來彌補(bǔ)。
所有權(quán)和職能
> Copy versus Move
在第一個(gè)示例中,我們首先將字符串文字(將其數(shù)據(jù)存儲(chǔ)在堆棧中)傳遞到函數(shù)foo()中。 在第二個(gè)示例中,我們將String類型(將數(shù)據(jù)存儲(chǔ)在堆中)傳遞給另一個(gè)函數(shù)foo()。 在main()和foo()的兩種實(shí)現(xiàn)中,我們都在各自的作用域中打印變量的內(nèi)存地址。
在第一個(gè)示例中,我們看到了與復(fù)制變量的值并將其綁定到新變量時(shí)類似的行為。 發(fā)生這種情況是因?yàn)樽址淖质褂昧硕褩!?存儲(chǔ)它們的指針?biāo)璧拇笮≡诰幾g時(shí)就已知道,因此,我們可以輕松地復(fù)制它的值并將其彈出到堆棧中。
這意味著每個(gè)函數(shù)main()和foo()都擁有存儲(chǔ)在字符串中的指針的副本。 當(dāng)foo()的作用域結(jié)束時(shí),foo()負(fù)責(zé)刪除其自己的字符串,當(dāng)main()的作用域結(jié)束時(shí),它也負(fù)責(zé)刪除其擁有的字符串。
另一方面,在第二個(gè)示例中,main()將字符串的所有權(quán)移到foo()中。 這意味著main()不再擁有字符串變量的所有權(quán),即它指向的內(nèi)存位置。 如果在移動(dòng)main()之后嘗試從內(nèi)部訪問字符串,則會(huì)收到錯(cuò)誤消息。
如示例注釋所示,Rust代替了復(fù)制(這可能會(huì)很昂貴),而是讓foo()負(fù)責(zé)內(nèi)存地址0x7efced01c010中的數(shù)據(jù)。 現(xiàn)在,只有當(dāng)foo()超出范圍時(shí),Rust才會(huì)釋放該地址處的內(nèi)存,從而使具有指向該地址的指針的其他任何變量無效。 再次,我們這樣做是為了避免雙重釋放錯(cuò)誤。
克隆,redux。
對(duì)于第二個(gè)示例,如果我們確實(shí)想復(fù)制string的值,以便main()和foo()都擁有自己的副本,類似于在堆棧上使用字符串文字時(shí),我們可以進(jìn)行"深層復(fù)制" ,通過使用clone()方法:
> clone()
在這里,如注釋所示,main()和foo()擁有各自的字符串副本的所有權(quán)。 盡管這是一個(gè)有效的解決方案,但它并不是最有效的,因?yàn)镽ust每次都需要逐步完成其堆分配過程。 有時(shí)您實(shí)際上確實(shí)希望兩個(gè)函數(shù)都與同一數(shù)據(jù)交互! (稍后會(huì)詳細(xì)介紹)。
賦予所有權(quán)
就像通過調(diào)用另一個(gè)函數(shù)并傳入變量來獲取所有權(quán)一樣,可以通過從其他函數(shù)返回來為函數(shù)賦予所有權(quán):
> Giving ownership
現(xiàn)在,foo()通過將字符串返回到調(diào)用foo()的位置來賦予main()所有權(quán)。 不出所料,只有當(dāng)main()的作用域結(jié)束時(shí),Rust才會(huì)釋放0x7fc98be1c010。
給予與接受
如果我們遵循這種趨勢(shì),那么我們既可以賦予所有權(quán),又可以通過接受并在foo()中返回相同的String類型來將所有權(quán)歸還給我們,這是有意義的:
> Passing ownership
但這僅僅是為了將值傳入和傳出函數(shù)而感到頭疼。 幸運(yùn)的是,Rust維護(hù)者已經(jīng)考慮到了這令人頭疼:
擁有所有權(quán)然后返回所有功能的所有權(quán)有點(diǎn)乏味。 如果我們想讓函數(shù)使用值而不是所有權(quán)怎么辦? 很煩人的是,除了我們可能還想返回的函數(shù)主體產(chǎn)生的任何數(shù)據(jù)之外,如果我們想再次使用它,也需要將返回的信息傳遞回去。 對(duì)我們來說幸運(yùn)的是,Rust具有此概念的功能,稱為參考。
—Rust書
應(yīng)用和借用
所有權(quán)可以容納數(shù)據(jù)的共享和傳遞,但是您必須遵循一些規(guī)則。
借用看起來像這樣:
main()使foo()可以訪問字符串,但是,(如標(biāo)簽所示)main()仍然是字符串的所有者。 這意味著在foo()作用域的末尾,不會(huì)從內(nèi)存中刪除字符串。 main()仍然負(fù)責(zé)字符串在內(nèi)存中的空間。
這是我們?nèi)绾卧赗ust中編寫交互的方法:
> Passing a reference/borrowing
就像我們的繪圖一樣,我們可以說main()將字符串的引用傳遞到foo()和foo()中,除了String類型引用。 用&符號(hào)表示。 在foo()的作用域結(jié)束之后,執(zhí)行返回到其調(diào)用方main(),并且字符串仍然有效。 foo()不必歸還所有權(quán),因?yàn)樗鼜奈幢皇谟杷袡?quán),它只是借來的。
"&"號(hào)表示引用,這些引用允許在不放棄所有權(quán)的情況下傳遞值! Rust知道,當(dāng)我們傳遞引用時(shí),所有權(quán)以及因此在內(nèi)存中分配該空間的責(zé)任仍然屬于原始所有者。
Rust允許我們創(chuàng)建任意數(shù)量的引用:
> Passing multiple references of the same value
無論我們傳遞多少次對(duì)字符串的引用,所有權(quán)都會(huì)恢復(fù)為其原始所有者。 (在這種情況下,所有權(quán)返回到最初實(shí)例化字符串的位置,但是請(qǐng)記住,我們可以傳遞所有權(quán)然后創(chuàng)建一個(gè)引用)。
可變性
最后要提到的是可變性。 Rust通常是以實(shí)用的風(fēng)格編寫的,但作者非常務(wù)實(shí),并了解現(xiàn)代語言并不總是如此黑白,因此Rust可以適應(yīng)可變性。
> mut
Rust允許我們使用mut關(guān)鍵字來使值可變。 注意內(nèi)存地址的更改,該更改指示必須重新分配字符串才能適合堆。
現(xiàn)在我們有了一個(gè)可變變量,我們就可以進(jìn)行可變引用了!
> Passing a mutable reference
這里的語法有點(diǎn)特定,但是我們看到首先需要聲明一個(gè)可變變量let mut string。 然后,當(dāng)我們使用&mut傳遞可變的引用時(shí)。 最后,我們?cè)诤瘮?shù)的簽名中使用&mut來明確聲明我們的函數(shù)接受可變引用。
現(xiàn)在,我們?nèi)匀豢梢源_保只負(fù)責(zé)main()負(fù)責(zé)字符串的解除分配,同時(shí)還允許其他函數(shù)對(duì)字符串進(jìn)行突變!
那些熟悉內(nèi)存管理的人可能會(huì)想到,如果不加以檢查,這將是多么危險(xiǎn)。 如果多個(gè)函數(shù)持有一個(gè)可變的引用并嘗試異步地同時(shí)更新同一內(nèi)存位置,將會(huì)發(fā)生什么情況? 例如在使用線程時(shí)? 這導(dǎo)致數(shù)據(jù)爭用情況。
當(dāng)兩個(gè)或多個(gè)線程可以訪問共享數(shù)據(jù)并且它們?cè)噲D同時(shí)更改它們時(shí),就會(huì)發(fā)生競(jìng)爭狀態(tài)。由于線程調(diào)度算法可以隨時(shí)在線程之間交換,因此您不知道線程嘗試訪問共享數(shù)據(jù)的順序。因此,數(shù)據(jù)更改的結(jié)果取決于線程調(diào)度算法,即兩個(gè)線程都在“競(jìng)爭”訪問/更改數(shù)據(jù)。
— Lehane和Amit Joki通過SO
當(dāng)使用低級(jí)語言(例如Rust)時(shí),此問題可能會(huì)加劇。 Rust允許我們?cè)L問原始指針,這可能會(huì)導(dǎo)致很多不安全的情況發(fā)生。
這是所有權(quán)要保護(hù)的一種事物,它通過執(zhí)行以下規(guī)則來做到這一點(diǎn):"在任何給定時(shí)間,您可以擁有一個(gè)可變引用或任意數(shù)量的不可變引用。"
具有此限制的好處是Rust可以防止在編譯時(shí)發(fā)生數(shù)據(jù)爭用。 數(shù)據(jù)爭用類似于爭用條件,并且在以下三種行為發(fā)生時(shí)發(fā)生:
-兩個(gè)或多個(gè)指針同時(shí)訪問相同的數(shù)據(jù)。
-至少有一個(gè)指針用于寫入數(shù)據(jù)。
-沒有用于同步數(shù)據(jù)訪問的機(jī)制。
數(shù)據(jù)爭用會(huì)導(dǎo)致未定義的行為,并且在嘗試在運(yùn)行時(shí)進(jìn)行跟蹤時(shí)可能會(huì)難以診斷和修復(fù); Rust不會(huì)發(fā)生此問題,因?yàn)樗踔敛粫?huì)通過數(shù)據(jù)競(jìng)爭來編譯代碼!
—Rust書
Rust的所有權(quán)規(guī)則再次得以解決,這被強(qiáng)調(diào)為Rust在其他系統(tǒng)語言之上提供的核心安全功能。 這意味著Ruby程序員和我一樣,仍然不必熟悉內(nèi)存管理的內(nèi)部工作!
懸掛引用
最后一件事,當(dāng)傳遞引用時(shí),還有另一種情況會(huì)導(dǎo)致錯(cuò)誤,稱為懸掛引用。
懸掛引用是指向已釋放數(shù)據(jù)的指針,例如:
> Dangling Reference — WILL NOT COMPILE!
在此示例中,foo()返回對(duì)字符串的引用。 但是,一旦foo()的作用域結(jié)束,便會(huì)釋放字符串的內(nèi)存,這意味著引用將指向內(nèi)存中的無效位置!
Rust在編譯時(shí)通過拋出錯(cuò)誤來防止這種情況。
類可以享受所有權(quán)模型的好處,而無需了解其提供的保護(hù)。 但是,能夠理解所有權(quán)只能解決的問題有助于編寫更好的代碼,而無需與編譯器抗?fàn)帯?/p>
關(guān)于Rust的所有權(quán)還有更多的東西要揭露,但是有了這兩篇文章,希望您有足夠的機(jī)會(huì)開始使用這種優(yōu)雅的解決方案來解決原本難以解決的問題。
參考資料
· Rust書
· 關(guān)于復(fù)制特性的Rust語言表單帖子
(本文翻譯自Thomas Countz的文章《Ownership in Rust, Part 2》,參考:https://medium.com/@thomascountz/ownership-in-rust-part-2-c3e1da89956e)
總結(jié)
以上是生活随笔為你收集整理的rust实现wss访问_Rust的所有权,第2部分的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 吴恩达 coursera AI 第三课总
- 下一篇: go修改服务器时间,Windows 配置