Rust и LLM: как пройти borrow checker, выбрать Rc или Arc и не утонуть в lifetimes — 12 промтов, проверенных на реальных проектах

Компилятор Rust ругается на заимствование, вы уже третий час смотрите на error[E0502] и начинаете подозревать, что владение — это проклятие. Знакомо? LLM может стать напарником, который объяснит ошибку, предложит решение и переведёт идиомы из C++ или Go. Но не всё так просто: модель иногда ошибается, и её ответы нужно проверять через cargo check и cargo clippy. В этой подборке — 12 промтов, которые я использую каждый день, чтобы быстрее проходить borrow checker, выбирать между Rc и Arc, разбираться с Send/Sync в async-коде и оптимизировать аллокации. Каждый промт — с примером входа и выхода, чтобы вы сразу видели, где LLM экономит часы, а где лучше перепроверить.

Промт 1: Разбор ошибки borrow checker с объяснением владения

Когда компилятор выдаёт ошибку заимствования, хочется просто попросить исправленный код. Но без понимания причины вы наступите на те же грабли снова. Этот промт заставляет LLM объяснить, что именно нарушено, и предложить варианты с разной степенью вмешательства.

Промт:

Ты — опытный Rust-разработчик. Объясни ошибку компилятора: [текст ошибки]. Вот код: [фрагмент кода]. Объясни, какое правило владения нарушено, и предложи 2-3 способа исправления: минимальное изменение, изменение с клонированием, изменение с рефакторингом. Для каждого варианта укажи плюсы и минусы. Подробнее — в статье «Rust — системное программирование: что стоит за курсом и как это работает на практике».

Пример входа:

fn main {
 let mut v = vec![1, 2, 3];
 let first = &v[0];
 v.push(4);
 println!("{}", first);
}

Ошибка: cannot borrowvas mutable because it is also borrowed as immutable.

Выход LLM (сокращённо):

  • Нарушено правило: нельзя иметь изменяемую ссылку, пока есть неизменяемая.
  • Вариант 1: убрать first до push — минимальное изменение.
  • Вариант 2: клонировать first (let first = v[0];) — просто, но копирование.
  • Вариант 3: использовать индексы или перестроить логику — без клонирования, но требует рефакторинга.

LLM хорошо объясняет, но проверяйте предложенный код через cargo check. Иногда модель предлагает решение, которое не компилируется из-за других ограничений.

Промт 2: Выбор между Box, Rc, Arc и Mutex/RwLock

Как только появляется необходимость разделять данные, начинается выбор: Box<T>, Rc<T>, Arc<T>, а может, Mutex или RwLock? Этот промт помогает взвесить все за и против.

Промт:

У меня есть структура [описание], которая должна использоваться в [контекст: многопоточно/однопоточно, с изменением/без]. Какой умный указатель выбрать: Box, Rc, Arc, Mutex, RwLock? Объясни trade-off по производительности, безопасности и удобству. Приведи пример кода с выбранным типом.

Пример входа: есть счётчик, который увеличивается из нескольких потоков.

Выход LLM: рекомендует Arc<Mutex<u32>>, объясняет, что Rc не потокобезопасен, Box не даёт разделения, RwLock избыточен для простого счётчика. Пример кода с Arc::new(Mutex::new(0)) и lock.unwrap.

Проверьте, что LLM не предлагает Rc в многопоточном контексте — это частая ошибка. Компилятор поймает, но лучше сразу отсеять.

Промт 3: Вывод lifetimes по сообщению компилятора

Lifetimes — боль новичков. Когда компилятор говорит missing lifetime specifier, хочется спросить, что это значит. Промт помогает интерпретировать сообщение и сгенерировать правильные аннотации.

Промт:

Компилятор Rust выдал ошибку: [текст ошибки с указанием lifetimes]. Вот функция: [код]. Объясни, какие lifetimes нужно добавить и почему. Покажи исправленный код с аннотациями и без них, если возможно. Подробнее — в статье «Go и Rust».

Пример входа:

fn longest(x: &str, y: &str) -> &str {
 if x.len > y.len { x } else { y }
}

Ошибка: missing lifetime specifier.

Выход LLM: объясняет, что возвращаемая ссылка может быть связана с любым из аргументов, поэтому нужно указать <'a> и привязать оба параметра и возвращаемое значение к одному lifetime. Пример: fn longest<'a>(x: &'a str, y: &'a str) -> &'a str. Также может предложить вернуть String, если lifetimes усложняются.

LLM часто правильно выводит lifetimes, но проверяйте, что аннотации не избыточны — иногда компилятор сам выводит их.

Промт 4: Перевод идиом из C++ и Go

Если вы переходите с C++ или Go, то привычные паттерны в Rust выглядят иначе. Промт помогает перевести идиому и объяснить разницу. Подробнее — в статье «Rust для Web (Actix/WASM)».

Промт:

Я привык к [идиома на C++/Go, например, RAII, умные указатели, горутины]. Как это делается в Rust? Переведи пример кода: [код на C++/Go]. Объясни ключевые отличия и подводные камни. Подробнее — в статье «Горутины не умирают молча».

Пример входа: C++ код с std::shared_ptr и std::mutex.

Выход LLM: предлагает Arc<Mutex<T>>, объясняет, что в Rust владение проверяется статически, поэтому гонки данных исключены на этапе компиляции. Упоминает, что в C++ можно забыть про блокировку, а в Rust — нет.

Сравнивайте сгенерированный код с идиоматичным Rust — иногда LLM тащит C++-стиль (например, избыточные clone).

Промт 5: Диагностика ошибок Send/Sync в async-коде и tokio

Async в Rust — отдельный уровень сложности. Ошибки future is not Send ставят в тупик. Промт помогает найти причину и исправить.

Промт:

В async-коде на tokio ошибка: future cannot be sent between threads safely. Вот код: [фрагмент]. Объясни, почему future не реализует Send, и предложи исправления: изменить типы, использовать spawn_local, обернуть в Mutex и т.д. Покажи исправленный код.

Пример входа: future захватывает Rc<RefCell<T>>.

Выход LLM: объясняет, что Rc не Send, поэтому future не может перемещаться между потоками. Предлагает заменить на Arc<Mutex<T>> или использовать tokio::task::spawn_local (если). Пример с Arc<Mutex<...>>.

Проверьте, что предложенный тип действительно Send — LLM иногда ошибается с RefCell.

Промт 6: Оптимизация аллокаций и трейтов

Когда производительность важна, каждый clone и Box имеет значение. Промт помогает найти лишние аллокации и выбрать трейты для обобщения.

Промт:

Проанализируй код на Rust на предмет лишних аллокаций и выбора трейтов: [код]. Предложи оптимизации: замена clone на заимствование, использование Cow, &str вместо String, трейты вместо конкретных типов. Объясни, как это повлияет на производительность и читаемость.

Пример входа: функция, которая принимает String и возвращает String, но могла бы принимать &str.

Выход LLM: предлагает изменить сигнатуру на fn process(s: &str) -> String, использовать to_owned только при необходимости. Упоминает Cow<str> для гибкости. Проверяйте бенчмарками — не все оптимизации дают выигрыш.

Промт 7: Генерация тестов для трейтов и обобщений

Rust поощряет обобщённое программирование, но тестировать трейты бывает сложно. Промт создаёт тесты для трейтов с разными реализациями.

Промт:

У меня есть трейт [название] с методами [список]. Напиши тесты на Rust, которые проверяют его для нескольких реализаций: [перечислить типы]. Используй mock-объекты, если нужно. Убедись, что тесты покрывают граничные случаи.

Пример входа: трейт Shape с методом area, реализации Circle и Square.

Выход LLM: создаёт тестовые функции с assert_eq!, проверяет площадь для разных значений, включая нулевые и отрицательные (если нужно). Может использовать mockall для сложных случаев.

Проверьте, что тесты компилируются и не содержат логических ошибок.

Промт 8: Объяснение сложных ошибок трейтов (object safety, coherence)

Ошибки вроде the trait cannot be made into an object или conflicting implementations сбивают с толку. Промт разбирает причину и предлагает обходные пути.

Промт:

Ошибка компилятора Rust: [текст ошибки, связанной с трейтами]. Вот код: [фрагмент]. Объясни, почему трейт не может быть объектом или почему конфликтуют реализации. Предложи решения: изменить трейт, использовать enum dispatch, добавить where-клаузы.

Пример входа: попытка создать Box<dyn Trait> для трейта с обобщённым методом.

Выход LLM: объясняет, что трейт не object-safe из-за generic-метода. Предлагает заменить на конкретный тип или использовать enum. Показывает пример с enum.

Промт 9: Рефакторинг кода с умными указателями

Когда код уже работает, но использует Rc<RefCell<T>> везде, хочется упростить. Промт помогает заменить умные указатели на более подходящие.

Промт:

Проанализируй код: [код с Rc/RefCell/Arc/Mutex]. Предложи рефакторинг для уменьшения количества умных указателей и упрощения владения. Объясни, какие можно заменить на ссылки, какие на Box, а какие оставить. Покажи улучшенный код.

Пример входа: структура с Rc<RefCell<Vec<T>>>, которая используется в одном потоке.

Выход LLM: предлагает заменить на RefCell<Vec<T>> или вообще на обычный Vec<T> с передачей по ссылке. Объясняет, что Rc нужен только для разделения владения.

Промт 10: Перевод с C++/Go на Rust с сохранением идиом

Если у вас есть код на C++ или Go, который нужно переписать на Rust, промт поможет сделать это идиоматично, а не просто транслитерировать.

Промт:

Переведи следующий код с [C++/Go] на Rust, сохраняя идиомы и избегая типичных ошибок. Код: [фрагмент]. Обрати внимание на владение, lifetimes и обработку ошибок. Предложи два варианта: максимально близкий к оригиналу и идиоматичный Rust.

Пример входа: Go-код с горутинами и каналами.

Выход LLM: предлагает использовать tokio и mpsc каналы, объясняет, что в Rust потоки и каналы работают иначе. Показывает два варианта.

Промт 11: Поиск утечек памяти и циклических ссылок с Rc/RefCell

Rc и RefCell могут привести к утечкам из-за циклов. Промт помогает найти такие места.

Промт:

Проверь код на наличие циклических ссылок с Rc/RefCell и потенциальных утечек памяти: [код]. Объясни, как их обнаружить и исправить с помощью Weak. Покажи исправленный код.

Пример входа: структура Node, которая хранит Rc<RefCell<Node>> на родителя и детей.

Выход LLM: указывает, что ссылка на родителя должна быть Weak, чтобы избежать цикла. Пример с Weak<RefCell<Node>> и upgrade.

Промт 12: Генерация макросов для повторяющегося кода

Макросы в Rust — мощный инструмент, но писать их сложно. Промт помогает сгенерировать макрос для повторяющихся паттернов.

Промт:

Напиши макрос на Rust для [описание задачи, например, генерации impl для нескольких типов]. Вот пример повторяющегося кода: [фрагмент]. Объясни, как работает макрос, и покажи его использование.

Пример входа: нужно реализовать трейт Display для нескольких структур.

Выход LLM: предлагает macro_rules! impl_display с шаблоном, объясняет синтаксис. Пример вызова для двух структур.

Проверьте, что макрос не создаёт конфликтов и компилируется.

Как не попасть в ловушку LLM

LLM — мощный помощник, но не заменитель компилятора. Всегда проверяйте сгенерированный код через cargo check и cargo clippy. Модель может предложить неоптимальное решение, пропустить lifetimes или предложить несуществующий метод. Особенно осторожно с async: ошибки Send/Sync часто требуют глубокого понимания, и LLM может дать поверхностный ответ.

Используйте промты как отправную точку, а не как готовый код. Чем точнее вы опишете контекст (версия Rust, крейты, ограничения), тем полезнее будет ответ. И помните: borrow checker — это друг, который помогает избежать ошибок. LLM может объяснить его логику, но не отменит правил. Подробнее — в статье «Borrow checker, async и трейты».

Если вы только начинаете, попробуйте промт 1 и 2 — они дают базу. Для async-разработки пригодится промт 5. А для оптимизации — 6 и 9. Комбинируйте, адаптируйте под свою задачу и не бойтесь перепроверять.

← Все статьи

Спросить AI по этой статье

Задайте вопрос по этой статье — ASI Biont ответит, опираясь на её текст.

Читайте также

Сколько времени нужно на иностранный язык: как ИИ и кривая забывания дают разговорный английский за год при 20 минутах в день

27 октября 2026

Цифровая гигиена за вечер: как найти утечки персональных данных и удалить информацию о себе из интернета

27 октября 2026

Сколько часов учить испанский до DELE 2026: разбор нормативов, статистики и гибридной подготовки с AI-репетитором

27 октября 2026

Юнит-тесты на Python с LLM: 12 промтов от pytest-фикстур до Hypothesis, которые закрывают непокрытые ветки

27 октября 2026

QA Automation Engineer: 5 мифов, которые стоят новичкам месяцев (и как их избежать)

11 ноября 2026

Вайб кодинг: как создавать код с помощью AI без навыков программирования — обзор курса и практические грабли

27 октября 2026

Full-Stack AI Engineer: как RAG-пайплайны, векторные базы данных и развертывание LLM на самом деле работают в продакшене

28 октября 2026

Сверка актов вручную или промптом: где LLM экономит бухгалтеру часы, а где создаёт риски

12 ноября 2026

Основы Computer Science: алгоритмы и структуры данных — кому подходит курс, а кому нет

11 ноября 2026