تغییرپذیری داخلی

در برخی موقعیت‌ها، لازم است که داده‌های پشت یک ارجاع اشتراکی (فقط خواندنی) را تغییر دهید. به عنوان مثال، یک ساختار داده اشتراکی ممکن است دارای یک کش داخلی باشد و بخواهد این کش را از روش‌های خواندنی به‌روزرسانی کند.

الگوی "تغییرپذیری داخلی" (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.

These types perform dynamic checks using a counter in the RefCell to prevent existence of a RefMut alongside another Ref/RefMut.

با پیاده‌سازی DerefDerefMut برای 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:?}");
}
This slide should take about 10 minutes.

مهم‌ترین نکته‌ای که باید از این اسلاید برداشت کرد این است که Rust روش‌های ایمن برای تغییر داده‌های پشت یک ارجاع اشتراکی ارائه می‌دهد. راه‌های مختلفی برای تضمین این ایمنی وجود دارد، و RefCell و Cell دو مورد از آن‌ها هستند.

  • RefCell قوانین معمول ارجاع Rust (یا چندین ارجاع اشتراکی یا یک ارجاع انحصاری) را با یک بررسی در زمان اجرا اعمال می‌کند. در این حالت، همه ارجاعات بسیار کوتاه هستند و هرگز هم‌پوشانی ندارند، بنابراین بررسی‌ها همیشه موفقیت‌آمیز هستند.

    • بلوک اضافی در مثال RefCell برای پایان دادن به ارجاعی که توسط فراخوانی borrow_mut ایجاد شده است، قبل از چاپ cell است. تلاش برای چاپ یک RefCell که در حال حاضر ارجاع داده شده است، فقط پیغام "{borrowed}" را نشان می‌دهد.
  • Cell یک روش ساده‌تر برای تضمین ایمنی است: این نوع دارای متدی به نام set است که self& را می‌پذیرد. این روش نیاز به بررسی در زمان اجرا ندارد، اما نیاز به انتقال مقادیر دارد که می‌تواند هزینه‌های خود را داشته باشد.

  • هر دو RefCell و Cell دارای !Sync هستند، به این معنی که RefCell& و &Cell نمی‌توانند بین نخ‌ها منتقل شوند. این امر مانع از دسترسی همزمان دو نخ به سلول می‌شود.