Введение#
Сегодня нам предстоит разобраться с темой заимствования. Как безопасно работать с данными без передачи владения.
Но прежде чем погружаться в эту тему разберём, что такое ссылка и её базовый синтаксис.
Что такое ссылки в Rust?#
В Rust ссылка & (reference) — это указатель на значение, который не владеет данными. Рассмотрим базовый пример:
let x = 42; // Создаём значение
let x_ref = &x; // Создаём ссылку (заимствование)
println!("{}", *x_ref); // Доступ к значению через *
Ключевые особенности ссылок#
- Нулевая стоимость — ссылки не копируют данные, работая аналогично указателям (но с проверками безопасности)
- Существует два типа ссылок:
&T
— неизменяемая ссылка (read-only)только чтение
&mut T
— изменяемая ссылка (read-write)чтение и запись
- Явное разыменование:
*x_ref
— получает значение (аналогично C++, но с проверками)
Важно! В большинстве случаев Rust сам разыменовывает ссылки автоматически. Например, в
println!("{}", x_ref)
символ*
не требуется.
Зачем нужно заимствование?#
Заимствование в языке Rust (borrowing) — это механизм, позволяющий временно использовать данные, не становясь их владельцем.
Система заимствования решает три ключевые задачи:
- Исключает гонки данных (data races) в многопоточном коде.
- Гарантирует безопасность памяти без сборщика мусора
- Избегает лишнего копирования больших структур данных
В отличие от владения (ownership), заимствование не передаёт права на использование и освобожение памяти — оно лишь даёт доступ к данным на время.
Неизменяемые ссылки (&T) в Rust#
Аналогия из жизни: чтение книги в библиотеке. Вы можете только прочитать её, но не изменять.
Практический пример#
fn calculate_length(s: &String) -> usize {
s.len() // Чтение без изменения
}
Правила работы с &T:
- Можно создавать сколько угодно неизменяемых ссылок одновременно.
- При существовании
&T
изменяемые ссылки (&mut T
) запрещены - Нельзя изменять данные через
&T
Изменяемые ссылки (&mut T) в Rust#
Аналогия из жизни: Эксклюзивный доступ к документу для редактирования - только один автор может изменять данные.
Пример использования#
fn add_suffix(s: &mut String) {
s.push_str("_suffix");
}
let mut data = String::from("text");
add_suffix(&mut data);
Жёсткие ограничения:
- Только одна изменяемая ссылка в одной области видимости.
- Нельзя одновременно иметь
&mut T
и&T
. - Гарантирует отсутствие гонок данных.
Ошибки заимствования: как их избежать#
Компилятор Rust предотвращает распространённые ошибки:
let mut vec = vec![1, 2, 3];
let first = &vec[0]; // Неизменяемая ссылка
vec.push(4); // Ошибка! Попытка изменения
Решение: ограничить область видимости ссылки или переписать код, чтобы не нужно было изменять исходную коллекцию.
Время жизни (Lifetimes) и заимствование#
Время жизни ('a
) определяет, как долго ссылка остаётся действительной:
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
Разберем чуть подробнее:
'a
— время жизни, общее дляx
,y
и возвращаемого значения.- Компилятор гарантирует, что возвращаемая ссылка будет валидной.
В следующих статьях я еще вернусь к теме времени жизни потому что часто программисты сталкиваются с ошибками в этой части.
Практические советы по заимствованию#
- Всегда начинайте с
&T
по умолчанию — если не нужно изменять данные - Избегайте
&mut T
в больших областях видимости
— это ограничивает гибкость - Доверяйте компилятору - если код компилируется, заимствование безопасно Он не даст вам запустить программу где произойдет ошибка при заимствовании
- Учитесь читать вывод компилятора об ошибках - он точно указывазывает на проблемы в коде
Заключение#
Заимствование в Rust — это то без чего этот язык сложно представить. Как и с владением вам предстоит постоянно с ним сталкиваться и работать. Эти концепции заложены в язык и благодаря им невозможно будет освободить память дважды, не будет висячих указателей, сборка мусора не требуется, а также у программы будет предсказуемое поведение в рантайме.
Поверьте это большие проблемы в приложениях написанных на других языках. Множество уязвимостей связанно как с переполнением буфера, так и в отказе от обслуживания из-за висячих указателей. (В будущем я еще напишу об этих темах отдельно)
Я советую вам придумать для себя аналогии которые близки вашему представлению и образу жизни и тогда восприниматься этот механизм работы с памятью в языке Rust будет проще.
На сегодня это все!