Умные указатели
Указатель — общее название переменной, которая хранит адрес в памяти. Этот
адрес ссылается, или «указывает», на какие-то другие данные. Самый
распространённый вид указателя в Rust — ссылка, с которой вы познакомились в
главе 4. Ссылки обозначаются символом & и заимствуют значение, на которое
указывают. Никаких особых возможностей, кроме обращения к данным, у них нет,
зато нет и накладных расходов.
Умные указатели (smart pointers) — это структуры данных, которые ведут себя как указатель, но вдобавок хранят метаданные и обладают дополнительными возможностями. Умные указатели придуманы не в Rust: они появились в C++ и есть в других языках. В стандартной библиотеке Rust определено несколько умных указателей, которые умеют больше, чем ссылки. Чтобы разобраться в общей идее, рассмотрим несколько примеров умных указателей, в том числе тип с подсчётом ссылок (reference counting). Такой указатель позволяет данным иметь нескольких владельцев: он следит за количеством владельцев и, когда их не остаётся, освобождает данные.
Поскольку в Rust есть владение и заимствование, между ссылками и умными указателями есть ещё одно различие: ссылки только заимствуют данные, а умные указатели во многих случаях владеют данными, на которые указывают.
Умные указатели обычно реализуют в виде структур. Но, в отличие от обычной
структуры, умные указатели реализуют трейты Deref и Drop. Трейт Deref
позволяет экземпляру умного указателя вести себя как ссылка, так что код можно
писать так, чтобы он работал и со ссылками, и с умными указателями. Трейт
Drop позволяет задать код, который выполняется, когда экземпляр умного
указателя выходит из области видимости. В этой главе мы разберём оба трейта и
покажем, почему они так важны для умных указателей.
Умный указатель — общий шаблон проектирования, который часто встречается в Rust, поэтому мы не будем разбирать все существующие умные указатели. У многих библиотек есть собственные, и вы даже можете написать свой. Мы рассмотрим самые распространённые умные указатели стандартной библиотеки:
Box<T>— для размещения значений в куче;Rc<T>— тип с подсчётом ссылок, позволяющий иметь нескольких владельцев;RefCell<T>— тип, который проверяет правила заимствования не при компиляции, а во время выполнения; доступ к значению внутри него даютRef<T>иRefMut<T>.
Кроме того, мы разберём шаблон внутренней изменяемости (interior mutability), когда неизменяемый тип предоставляет API для изменения внутреннего значения. А ещё обсудим циклы ссылок: как они приводят к утечкам памяти и как их избежать.
Начнём!