Внутрішня мутабельність

У деяких ситуаціях необхідно модифікувати дані за спільним (доступним лише для читання) посиланням. Наприклад, структура даних зі спільним доступом може мати внутрішній кеш, і ви хочете оновити цей кеш методами, доступними лише для читання.

Паттерн "внутрішня мутабельність" дозволяє ексклюзивний (мутабельний) доступ за спільним посиланням. Стандартна бібліотека надає декілька способів зробити це, забезпечуючи при цьому безпеку, як правило, шляхом виконання перевірки під час виконання.

RefCell

use std::cell::RefCell;

fn main() {
    // Зауважте, що `cell` НЕ оголошено як мутабельну.
    let cell = RefCell::new(5);

    {
        let mut cell_ref = cell.borrow_mut();
        *cell_ref = 123;

        // Це спричиняє помилку під час виконання.
        // let other = cell.borrow();
        // println!("{}", *other);
    }

    println!("{cell:?}");
}

Cell

Cell обгортає значення і дозволяє отримати або встановити значення, навіть за допомогою спільного посилання на Cell. Однак, він не дозволяє посилатися на значення. Оскільки посилань немає, правила запозичення не можуть бути порушені.

use std::cell::Cell;

fn main() {
    // Зауважте, що `cell` НЕ оголошено як мутабельну.
    let cell = Cell::new(5);

    cell.set(123);
    println!("{}", cell.get());
}
This slide should take about 10 minutes.

Основне, що можна винести з цього слайду, це те, що Rust надає безпечні способи модифікації даних за спільним посиланням. Існує безліч способів забезпечити цю захищеність, і RefCell та Cell - два з них.

  • RefCellзастосовує звичайні правила запозичень Rust (або декілька спільних посилань, або одне ексклюзивне посилання) з перевіркою під час виконання. У цьому випадку всі запозичення дуже короткі і ніколи не перетинаються, тому перевірки завжди проходять успішно.

    • Додатковий блок у прикладі RefCell призначений для завершення запозичення, створеного викликом borrow_mut, до того, як ми надрукуємо комірку. Спроба надрукувати запозичену комірку RefCell просто покаже повідомлення "{borrowed}".
  • Cell є найпростішим засобом гарантування безпеки: він має метод set, який приймає значення &self. Це не потребує перевірки під час виконання, але вимагає переміщення значень, що може мати свою ціну.

  • І RefCell, і Cell є !Sync, що означає, що &RefCell і &Cell не можна передавати між потоками. Це запобігає спробам двох потоків одночасно отримати доступ до комірки.