Блог / Rust

Управление памятью в Rust:
мифы и реальность

Разбираем работу Borrow Checker на примере кэширования данных в реальном приложении. Почему RefCell спасает, когда Rc не справляется.

Автор: Алексей Д. • 12 мин чтения

01. Введение

Rust обещает безопасность памяти без сборщика мусора. Звучит как магия, пока вы не сталкиваетесь с компилятором, который отказывается собирать ваш код, потому что вы пытались изменить данные, на которые есть несколько ссылок.

В этой статье мы рассмотрим конкретный кейс: реализацию простого кэша конфигурации, который должен быть доступен для чтения из нескольких потоков, но обновляться только из одного места. Мы пройдем путь от наивного использования Option до безопасной мутации через RefCell и Cell.

> "Если вы чувствуете, что хотите использовать unsafe, чтобы обойти заимствование — остановитесь. Rust уже нашел решение лучше, вы просто еще не узнали его."

02. Технический разбор

Начнем с проблемы. У нас есть структура ConfigStore. Мы хотим хранить в ней значение типа String. В C++ мы бы просто передавали указатель. В Rust мы должны явно указать: кому принадлежит память и кто может ее изменять.

Сценарий А: Rc

Rc (Reference Counted) позволяет иметь множественное владение данными. Но есть нюанс: Rc неизменяем по умолчанию.

error[E0594]: cannot assign to `*data`, which is declared as read-only

Сценарий Б: RefCell

Решение — перенести проверку заимствований со времени компиляции на время выполнения. RefCell позволяет менять данные внутри, даже если сам контейнер не мутируемый.

let mut store = RefCell::new("v1.0".to_string());
*store.borrow_mut() = "v1.1".to_string();

Ключевое отличие: Rc считает количество ссылок, а RefCell следит за тем, чтобы в момент мутации не было активных читалок (borrow). Если попытаться нарушить правила, программа паникует (panic) в рантайме, но компилятор пропустит код. Это цена, которую мы платим за гибкость.

03. Выводы

Управление памятью в Rust — это не про то, как освободить байты. Это про то, как выразить намерения вашей программы на языке типов.

  • 01. Используйте Copy для простых примитивов (int, bool), чтобы избежать накладных расходов.
  • 02. Если данные читаются многими, но меняются редко — Rc> отличный выбор для синглотон-паттернов в однопоточном окружении.
  • 03. Не бойтесь clone() для небольших структур. Это часто дешевле, чем усложнять архитектуру заимствований.
0
Утечек памяти
O(1)
Доступ к RefCell
100%
Безопасность

Хотите разбираться глубже?

В курсе "Rust Internals" мы разбираем 15 кейсов управления памятью и аллокации кучи.