Представьте ситуацию: у вас есть данные в куче (heap) и вы хотите, чтобы несколько объектов могли их использовать. Но Rust не любит ситуации когда неясно кто владеет объектом. Именно тогда на сцену выходит Rc<T>.
Rc<T> — это умный указатель, реализующий счётчик ссылок (shared ownership). При клонировании Rc<T> вы не копируете данные, а просто увеличиваете счётчик владельцев. Это эффективно, но только для однопоточного кода.
Почему бы не использовать &T ?
&T — это заимствование, но оно не даёт владения. Ссылка жёстко ограничена временем жизни (lifetimes) об этом еще поговорим, её нельзя сохранять в структурах без явной аннотации ‘a. Rc<T> решает эту проблему и даёт вам контроль и гибкость.
В проекте у нас может быть структура City, в которой хранятся данные о населении и общее описание города. Это описание может использоваться также в других местах: например, в туристическом справочнике или для отчёта. Вместо копирования строки мы хотим её разделить.
Пример c городами и историей
Шаг 1/2
1#[derive(Debug)] 2structCity{ 3name: String, 4history: String, 5} 6 7#[derive(Debug)] 8structCitySummary{ 9headline: String,10description: String,11}1213fnmain(){14lethistory=String::from("Город основан в 1574 году");1516letcity=City{17name: String::from("Уфа"),18history: history,19};2021letsummary=CitySummary{22headline: String::from("Уфа"),23description: history,// ❌ перемещено из-под city
24};2526println!("{:?}, {:?}",city,summary);27}
1. Ошибка: попытка поделиться строкой напрямую между структурами
Мы либо копируем строку, либо нарушаем правила владения, переместив значение. ⚠️ Данные не могут быть одновременно в двух структурах.
С Rc можно безопасно использовать одни и те же данные в нескольких структурах. Владение разделено, а данные освобождаются когда последний владелец исчезает.
Что делать, если нужно изменять данные? Встроить RefCell<T> в Rc. Тогда получим внутреннюю мутабельность с проверкой во время выполнения. Это даёт гибкость, но требует внимательности.
RefCell вызывает панику при попытке взять два borrow_mut() сразу. Ошибка не в синтаксисе, а в логике. Это помогает обеспечить безопасность в рантайме, когда нельзя использовать обычные ссылки.
Пример общего изменяемого состояния через Rc + RefCell#
Rc это ещё один из важных механизмов языка Rust. Он не позволяет расширяет стандартную модель владения, а также предоставляет дополнительный инструмент для безопасной и выразительной работы с памятью. Используйте Rc там, где необходимо разделить владение между несколькими объектами которым требуется доступ к данным.
Не забывайте, что Rc используется для подсчета ссылок в однопоточном коде. ❗️
Если вам нужно работать в нескольких потоках - для этого используется Arc, но об этом вы узнаете в следующих статьях.