在Rust中,我們從引用借用的規則中知道有不可變(共享)引用和可變(獨占)引用。
如果我們有一個共享引用,我們可以想要多少就有多少。這是因為這些引用不允許我們改變它們指向的值,所以同時有多個引用是可以的。
可變引用則不然,顧名思義,可變引用允許我們改變它們所指向的值。所以在這種情況下,對值有多個引用是不行的。例如,考慮兩個線程,其中每個線程都持有一個獨占引用并同時更改其值。線程運行后的值應該是什么?確切地說,這是一個未定義的行為!
那么為什么Rust允許我們擁有可共享的可變容器呢?這不是打破了Rust的借用規則嗎?這是因為這些容器有限制,允許以安全的方式使用它們,同時仍然提供允許可變的api。這就是為什么這些類型提供“內部可變性”,當使用它們時,它們作為容器施加限制,在這些限制下,它們所持有的類型可以被安全地修改!
基本上,Cell通過確保沒有指向其保存的數據的指針并且在單線程環境中執行來實現這一點。
有了這些限制,更改Cell中的數據是完全可以的。想想看,如果我們知道Cell中沒有指向數據的指針,并且它不是跨線程共享的,則可以保證我們對它具有獨占訪問權。
現在的問題是,Cell是如何施加這些約束的?Cell通過從不返回對其內部數據的引用來實現這一點,它總是返回數據的副本。因此,這已經告訴Cell適用于內存開銷小的類型,例如整數。
此外,Cell沒有實現Sync,因此它不能在線程邊界之間共享。
Cell的構建塊是UnsafeCell,這是Rust內部可變性的構建塊之一。UnsafeCell允許我們在任何時候獲得一個原始的獨占指針,指向它所保存的數據。這當然是一個不安全的操作,所以我們必須在unsafe{}塊中進行操作。
Cell的一種可能的簡化實現是:
use std::cell::UnsafeCell;struct Cell<T> { value: UnsafeCell<T>}// 禁止跨線程使用Cellimpl<T> !Sync for Cell<T> {}impl<T> Cell<T> { pub fn new(value: T) -> Self { Cell { value: UnsafeCell::new(value) } } pub fn set(self, value: T) { // 用一個新值覆蓋單元格所指向的值 unsafe { *self.value.get() = value } } pub fn get(&self) -> T where T: Copy { // 返回Cell所指向的數據的副本 unsafe { *self.value.get() } }}
這里我們使用UnsafeCell來存儲Cell的數據,不允許在線程之間共享此類型,最后,我們從不引用Cell中的數據。注意,get方法只適用于實現Copy的類型,并且返回內部類型的副本。
在本文中,我們探討了Rust的Cell類型,我們了解到Cell通過對其持有的數據施加約束來允許內部可變性。
本文鏈接:http://www.tebozhan.com/showinfo-26-99019-0.html深入研究Rust的內部可變性- Cell是如何工作的?
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com