Перейти к основному содержимому
  1. Rust/

Заимствование (Borrowing) в Rust

651 слово·4 минут· loading · loading · ·
Rust Dev
about-rust - Эта статья часть цикла.
Часть 15: Эта статья

Введение
#

Сегодня нам предстоит разобраться с темой заимствования. Как безопасно работать с данными без передачи владения.

Но прежде чем погружаться в эту тему разберём, что такое ссылка и её базовый синтаксис.

Что такое ссылки в Rust?
#

В Rust ссылка & (reference) — это указатель на значение, который не владеет данными. Рассмотрим базовый пример:

let x = 42;          // Создаём значение
let x_ref = &x;      // Создаём ссылку (заимствование)
println!("{}", *x_ref); // Доступ к значению через * 

Ключевые особенности ссылок
#

  • Нулевая стоимость — ссылки не копируют данные, работая аналогично указателям (но с проверками безопасности)
  • Существует два типа ссылок:
  1. &T — неизменяемая ссылка (read-only) только чтение
  2. &mut T — изменяемая ссылка (read-write) чтение и запись
  • Явное разыменование:
    • *x_ref — получает значение (аналогично C++, но с проверками)

Важно! В большинстве случаев Rust сам разыменовывает ссылки автоматически. Например, в println!("{}", x_ref) символ * не требуется.

Совет: Хотите глубже понять основы? Прочитайте предыдущую статью перед продолжением.

Зачем нужно заимствование?
#

Заимствование в языке Rust (borrowing) — это механизм, позволяющий временно использовать данные, не становясь их владельцем.

Система заимствования решает три ключевые задачи:

  1. Исключает гонки данных (data races) в многопоточном коде.
  2. Гарантирует безопасность памяти без сборщика мусора
  3. Избегает лишнего копирования больших структур данных

В отличие от владения (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 и возвращаемого значения.
  • Компилятор гарантирует, что возвращаемая ссылка будет валидной.

В следующих статьях я еще вернусь к теме времени жизни потому что часто программисты сталкиваются с ошибками в этой части.

Практические советы по заимствованию
#

  1. Всегда начинайте с &T по умолчанию — если не нужно изменять данные
  2. Избегайте &mut T в больших областях видимости — это ограничивает гибкость
  3. Доверяйте компилятору - если код компилируется, заимствование безопасно Он не даст вам запустить программу где произойдет ошибка при заимствовании
  4. Учитесь читать вывод компилятора об ошибках - он точно указывазывает на проблемы в коде

Заключение
#

Заимствование в Rust — это то без чего этот язык сложно представить. Как и с владением вам предстоит постоянно с ним сталкиваться и работать. Эти концепции заложены в язык и благодаря им невозможно будет освободить память дважды, не будет висячих указателей, сборка мусора не требуется, а также у программы будет предсказуемое поведение в рантайме.

Поверьте это большие проблемы в приложениях написанных на других языках. Множество уязвимостей связанно как с переполнением буфера, так и в отказе от обслуживания из-за висячих указателей. (В будущем я еще напишу об этих темах отдельно)

Я советую вам придумать для себя аналогии которые близки вашему представлению и образу жизни и тогда восприниматься этот механизм работы с памятью в языке Rust будет проще.

На сегодня это все!

about-rust - Эта статья часть цикла.
Часть 15: Эта статья

Связанные статьи

Hash Map в Rust
733 слов·4 минут· loading · loading
Rust Dev
Rust императивный и функциональный, а также о методе collect
1030 слов·5 минут· loading · loading
Rust Dev
Pattern matсhing
606 слов·3 минут· loading · loading
Rust Dev