تغییرپذیری داخلی
در برخی موقعیتها، لازم است که دادههای پشت یک ارجاع اشتراکی (فقط خواندنی) را تغییر دهید. به عنوان مثال، یک ساختار داده اشتراکی ممکن است دارای یک کش داخلی باشد و بخواهد این کش را از روشهای خواندنی بهروزرسانی کند.
الگوی "تغییرپذیری داخلی" (interior mutability) اجازه میدهد که دسترسی انحصاری (قابل تغییر) پشت یک ارجاع اشتراکی وجود داشته باشد. کتابخانه استاندارد روشهای متعددی برای انجام این کار فراهم میکند، در حالی که همچنان ایمنی را تضمین میکند، معمولاً با انجام یک بررسی در زمان اجرا.
Cell
Cell
wraps a value and allows getting or setting the value using only a shared reference to the Cell
. However, it does not allow any references to the inner value. Since there are no references, borrowing rules cannot be broken.
use std::cell::Cell; fn main() { // Note that `cell` is NOT declared as mutable. let cell = Cell::new(5); cell.set(123); println!("{}", cell.get()); }
RefCell
RefCell
allows accessing and mutating a wrapped value by providing alternative types Ref
and RefMut
that emulate &T
/&mut T
without actually being Rust references.
این type با استفاده از یک شمارنده در RefCell
بررسیهای dynamic را انجام میدهند تا از وجود RefMut
در کنار Ref
/RefMut
دیگر جلوگیری کنند.
با پیادهسازی Deref
(و DerefMut
برای RefMut
)، این تایپها امکان فراخوانی متدها روی مقدار داخلی را بدون اجازه خروج ارجاعها فراهم میکنند.
use std::cell::RefCell; fn main() { // Note that `cell` is NOT declared as mutable. let cell = RefCell::new(5); { let mut cell_ref = cell.borrow_mut(); *cell_ref = 123; // This triggers an error at runtime. // let other = cell.borrow(); // println!("{}", *other); } println!("{cell:?}"); }
مهمترین نکتهای که باید از این اسلاید برداشت کرد این است که Rust روشهای ایمن برای تغییر دادههای پشت یک ارجاع اشتراکی ارائه میدهد. راههای مختلفی برای تضمین این ایمنی وجود دارد، و RefCell
و Cell
دو مورد از آنها هستند.
-
RefCell
قوانین معمول ارجاع Rust (یا چندین ارجاع اشتراکی یا یک ارجاع انحصاری) را با یک بررسی در زمان اجرا اعمال میکند. در این حالت، همه ارجاعات بسیار کوتاه هستند و هرگز همپوشانی ندارند، بنابراین بررسیها همیشه موفقیتآمیز هستند.- بلوک اضافی در مثال
RefCell
برای پایان دادن به ارجاعی که توسط فراخوانیborrow_mut
ایجاد شده است، قبل از چاپcell
است. تلاش برای چاپ یکRefCell
که در حال حاضر ارجاع داده شده است، فقط پیغام"{borrowed}"
را نشان میدهد.
- بلوک اضافی در مثال
-
Cell
یک روش سادهتر برای تضمین ایمنی است: این نوع دارای متدی به نامset
است کهself&
را میپذیرد. این روش نیاز به بررسی در زمان اجرا ندارد، اما نیاز به انتقال مقادیر دارد که میتواند هزینههای خود را داشته باشد. -
هر دو
RefCell
وCell
دارای!Sync
هستند، به این معنی کهRefCell&
و&Cell
نمیتوانند بین نخها منتقل شوند. این امر مانع از دسترسی همزمان دو نخ به سلول میشود.