內部可變性 (Interior Mutability)
在某些情況下,您必須修改共用 (唯讀) 參照背後的資料:比方說,共用的資料結構可能含有內部快取,並想透過唯讀方法更新該快取。
「內部可變動性」模式可以在共用參照背後提供專屬 (可變動的) 存取權。標準程式庫支援以多種方式執行此操作,同時仍可確保安全,做法通常是執行執行階段檢查。
RefCell
use std::cell::RefCell; use std::rc::Rc; #[derive(Debug, Default)] struct Node { value: i64, children: Vec<Rc<RefCell<Node>>>, } impl Node { fn new(value: i64) -> Rc<RefCell<Node>> { Rc::new(RefCell::new(Node { value, ..Node::default() })) } fn sum(&self) -> i64 { self.value + self.children.iter().map(|c| c.borrow().sum()).sum::<i64>() } } fn main() { let root = Node::new(1); root.borrow_mut().children.push(Node::new(5)); let subtree = Node::new(10); subtree.borrow_mut().children.push(Node::new(11)); subtree.borrow_mut().children.push(Node::new(12)); root.borrow_mut().children.push(subtree); println!("graph: {root:#?}"); println!("graph sum: {}", root.borrow().sum()); }
Cell
Cell
會納入值,並允許取得或設定該值,即使具有對 Cell
的共用參照也一樣。但是,它不允許對該值進行任何參照。由於沒有參照,因此借用規則不得違反。
這張投影片的重點是 Rust 提供「安全的」方法,可讓您修改共用參照背後的資料。要確保安全性有許多方式,而 RefCell
和 Cell
是其中兩種方法。
-
RefCell
會透過執行階段檢查,強制使用 Rust 的一般借用規則 (多個共用參照或單一專屬參照)。在本例中,所有借用都非常短暫且永遠不會重疊,因此檢查一律會成功。 -
Rc
只允許對自身內容的共用 (唯讀) 存取行為,因為允許 (並計算) 多個參照才是它的用途。但是,由於我們要修改這個值,因此內部可變動性不可或缺。 -
如要確保安全,
Cell
是較簡單的做法,因為其中的set
方法可接受&self
。這無需動用執行階段檢查,但需要移動值,因此可能有其相應成本。 -
Demonstrate that reference loops can be created by adding
root
tosubtree.children
. -
如要演示執行階段發生的恐慌情形,請新增
fn inc(&mut self)
,這可讓self.value
遞增,並在其子項呼叫相同的方法。在有參照迴圈的情況下,這會引發恐慌,其中的thread 'main' 會因 'already borrowed: BorrowMutError'
而恐慌。