Rust без боли: 10 промтов, которые заменят вам месяц чтения документации и сэкономят нервы на borrow checker
Rust — язык, который входит в топ самых любимых языков программирования по опросам Stack Overflow много лет подряд. И одновременно это язык, с которым у большинства новичков отношения не складываются с первого раза. Причина банальна: компилятор Rust строг до безобразия, а borrow checker умеет превратить простую, казалось бы, программу в квест по разгадыванию загадок.
Хорошая новость в том, что с появлением качественных LLM-моделей (GPT-4o, Claude 3.5/3.7, Gemini 2.x) процесс обучения Rust ускорился в разы. Не потому, что ИИ пишет за вас код, а потому, что он умеет объяснять ошибки компилятора человеческим языком, показывать идиоматичные паттерны и разбирать чужой код строчка за строчкой. Плохая новость: большинство разработчиков используют ИИ неэффективно — просят "напиши мне функцию" вместо того, чтобы использовать модель как персонального ментора.
В этой подборке — 10 промтов, которые я собрал за последние полтора года практической работы с Rust и async-сервисами на Tokio. Каждый промт проверен на реальных проектах: HTTP-API, обработчики очередей, сетевые утилиты. Все они готовы к копипасту и работают с любой современной моделью, понимающей технический контекст.
1. Разбор ошибки компилятора с объяснением первопричины
Задача: компилятор выдал ошибку, вы её не понимаете, а гуглить 40 строк трейсбека лень.
Промт:
Ты — эксперт по Rust. Разбери ошибку компилятора ниже.
1) Объясни простыми словами, ЧТО именно не так и ПОЧЕМУ компилятор это запрещает.
2) Покажи минимальный воспроизводимый пример кода, который ломается.
3) Предложи 2-3 варианта исправления: от самого идиоматичного до самого простого.
4) Объясни, какой паттерн из Rust Book (глава, раздел) покрывает эту ситуацию.
5) В конце — короткое правило-мнемоника на будущее.
Ошибка компилятора:
<вставьте сюда вывод cargo build>
Мой код:
<вставьте сюда проблемный участок>
Пример использования. Классика: передаёте String в функцию, а потом пытаетесь его использовать — компилятор ругается на use of moved value. Модель объяснит, что String не реализует Copy, расскажет про borrow через &, clone() и Rc<RefCell<>> — и укажет соответствующий раздел Rust Book, глава 4 «Understanding Ownership».
Совет: всегда вставляйте полный вывод cargo build, а не только последнюю строку. Rust даёт очень информативные подсказки (help:), и модель должна их видеть.
2. Ownership и borrow checker через аналогии
Задача: вы понимаете синтаксис, но не понимаете, почему &mut не может coexist с &.
Промт:
Объясни правила ownership и borrowing в Rust через аналогии из реальной жизни (не из программирования).
Покрой:
- владение (ownership) и перемещение (move)
- заимствование (borrow) и изменяемое заимствование (&mut)
- правило "одна мутабельная ссылка ИЛИ много иммутабельных"
- времена жизни (lifetimes) и зачем они нужны
Для каждого правила — приведи пример кода, который НЕ компилируется, и рядом — исправленный вариант.
В конце — таблицу: правило
| аналогия | типичная ошибка новичка.
Пример использования. Аналогия, которая заходит почти всем: & — это как читательский билет в библиотеке (много людей могут читать одну книгу одновременно), а &mut — как редактор, который вносит правки (пока он работает, никто другой читать не может). Модель сгенерирует несколько таких аналогий и подкрепит кодом.
3. Идиоматичный рефакторинг «новичкового» кода
Задача: код работает, но выглядит как Java, переписанная на Rust.
Промт:
Перепиши мой Rust-код в идиоматичном стиле.
Требования:
- используй итераторы вместо ручных for-циклов с push
- замени unwrap() на обработку ошибок через ? и Result
- используй pattern matching там, где это уместно
- применяй derive-макросы вместо ручных impl
- не меняй публичный API без необходимости
Для каждого изменения объясни, ПОЧЕМУ так идиоматичнее.
В конце покажи diff до/после.
Код:
<вставьте код>
Пример использования. Типичный кейс: цикл for i in 0..vec.len() { result.push(vec[i] * 2); } превращается в `let result: Vec<_> = vec.iter().map(
|x| x * 2).collect();`. Модель объяснит, что итераторы в Rust компилируются в такой же машинный код, что и ручные циклы (zero-cost abstraction), но при этом безопаснее за счёт отсутствия индексов.
4. Генерация тестов, включая property-based
Задача: нужно покрыть модуль тестами, но лень писать 30 однотипных кейсов.
Промт:
Напиши тесты для функции ниже.
Требования:
1) Юнит-тесты через #[cfg(test)] mod tests.
2) Граничные случаи: пустой ввод, максимальные значения, невалидные данные.
3) Property-based тест через proptest или quickcheck: сформулируй инвариант, который всегда должен выполняться.
4) Тесты на panic-поведение (should_panic) там, где это уместно.
5) Комментарий к каждому тесту: что именно он проверяет.
Функция:
<вставьте код>
Пример использования. Для функции fn parse_port(s: &str) -> Result<u16, ParseError> модель предложит proptest-инвариант: «любая строка, которая не парсится как u16, должна возвращать Err, а не паниковать». Это ловит целый класс багов, которые обычные юнит-тесты пропускают.
5. Async/await и Tokio с нуля
Задача: вы поняли синхронный Rust и хотите перейти к async-сервисам.
Промт:
Объясни async/await в Rust и экосистему Tokio.
Структура ответа:
1) Чем async в Rust отличается от async в Python/JavaScript (Future — ленивые, poll-модель).
2) Что такое runtime и зачем нужен #[tokio::main].
3) Когда НЕ нужен async (CPU-bound задачи, короткие скрипты).
4) Минимальный HTTP-сервер на axum или actix-web с 2-3 роутами.
5) Обработка ошибок в async: почему std::error::Error и anyhow/thiserror.
6) Работа с блокирующим кодом: spawn_blocking и почему нельзя блокировать runtime.
Все примеры должны быть рабочими и на актуальных версиях крейтов на октябрь 2026.
Пример использования. Модель обычно подчёркивает ключевое отличие: Future в Rust — это ленивая структура, которая ничего не делает, пока её не опросят через .await. В Python async def запускается сразу. Это объясняет половину ошибок новичков при переходе с Python.
6. Обработка ошибок: thiserror vs anyhow
Задача: выбрать стратегию обработки ошибок и не утонуть в кастомных enum'ах.
Промт:
Помоги спроектировать обработку ошибок для проекта.
Контекст: <библиотека / приложение / HTTP-сервис>
Вопросы:
1) Когда использовать thiserror, а когда anyhow?
2) Как правильно комбинировать оба в одном проекте (библиотечные типы vs границы приложения)?
3) Как конвертировать ошибки из разных крейтов через #[from]?
4) Как не терять контекст: .context() из anyhow.
5) Как отдавать ошибки в HTTP-ответ с правильным status code (маппинг в axum/actix).
Покажи полный пример: кастомный Error enum, impl Display, impl std::error::Error через thiserror, использование в сервисе.
Пример использования. Практическое правило: thiserror — для публичных библиотек (пользователи должны видеть конкретные типы), anyhow — для бинарников и верхних слоёв приложения. Модель покажет, как на границе слоёв конвертировать anyhow::Error в HTTP-ответ с корректным статусом.
7. Работа с lifetime-ами в структурах
Задача: вы пишете парсер или zero-copy структуру и упираетесь в missing lifetime specifier.
Промт:
Разбери мой код и помоги расставить lifetimes.
1) Объясни, что lifetime — это НЕ время жизни значения, а аннотация для компилятора.
2) Покажи, какие связи между ссылками я должен выразить.
3) Предложи минимальный набор lifetime-параметров.
4) Если возможно — перепиши без явных lifetime (elision rules).
5) Если структура держит ссылку — объясни, почему это ограничивает её использование.
Код:
<вставьте код>
Пример использования. Классика: struct Parser<'a> { input: &'a str }. Модель объяснит, что lifetime здесь означает: «экземпляр Parser не может жить дольше, чем строка, на которую он ссылается». Часто решение — перейти на owned String или использовать Cow<'a, str>.
8. Оптимизация производительности и профилирование
Задача: код работает, но медленно, а вы не знаете, где узкое место.
Промт:
Помоги найти узкие места в моём Rust-коде.
1) Предложи инструменты профилирования: perf, flamegraph, cargo-flamegraph, criterion для бенчмарков.
2) Проанализируй код и укажи подозрительные места: лишние clone(), аллокации в цикле, dyn вместо generic, Vec вместо slice, неэффективные коллекции.
3) Напиши бенчмарк на criterion для критичных функций.
4) Предложи оптимизации по приоритету: сначала алгоритмические, потом микрооптимизации.
5) Покажи, как замерить улучшение.
Код:
<вставьте код>
Пример использования. Часто первая же найденная проблема — .clone() внутри горячего цикла. Модель покажет, как заменить на заимствование или Rc/Arc, и напишет бенчмарк, который докажет улучшение в цифрах.
9. Миграция с другого языка: Python/Go/TypeScript → Rust
Задача: вы опытный разработчик, но Rust для вас новый, и нужно быстро перенести логику.
Промт:
Я перехожу на Rust с <язык>. Помоги перевести код и объясни различия.
1) Переведи функцию/модуль ниже на идиоматичный Rust.
2) Покажи таблицу соответствий: <язык> → Rust (типы, коллекции, обработка ошибок, async).
3) Объясни, какие вещи в Rust делаются принципиально иначе (например, нет null — есть Option).
4) Укажи, какие паттерны из <язык> в Rust считаются антипаттернами.
Код:
<вставьте код>
Пример использования. Для Python-разработчиков модель подчеркнёт: нет исключений в привычном виде (есть Result), нет GIL-проблем (есть Send/Sync), нет динамической типизации, но есть очень мощная система trait'ов. Для Go — разница в обработке ошибок и отсутствии GC-пауз в том же смысле.
10. Генерация boilerplate для проекта
Задача: нужно быстро поднять скелет проекта с правильной структурой.
Промт:
Сгенерируй скелет Rust-проекта.
Тип: <CLI / HTTP-API / библиотека / воркер>
Стек: <tokio, axum, sqlx, serde, tracing>
Требования:
1) Структура директорий (src/, tests/, benches/, examples/).
2) Cargo.toml с актуальными версиями крейтов.
3) Модульная структура: main.rs + lib.rs + модули по слоям.
4) Базовая обработка ошибок (thiserror/anyhow).
5) Логирование через tracing + tracing-subscriber.
6) Конфигурация через env + serde.
7) README с командами cargo run/test/clippy/fmt.
Все зависимости — на актуальных версиях. Без deprecated API.
Пример использования. Для HTTP-сервиса модель сгенерирует слоистую структуру: handlers/, services/, repositories/, models/, errors.rs, config.rs. Это экономит час-полтора рутинной работы на старте проекта.
Бонус: как использовать промты эффективнее
Пара практических правил, которые я вывел за год работы:
| Правило | Почему работает |
|---|---|
| Всегда вставляйте полный контекст (версии, вывод cargo build, Cargo.toml) | Модель не угадывает API крейтов, она их знает, но нужны версии |
| Просите объяснять «почему», а не только «как» | Это единственный способ реально выучить Rust, а не копипастить |
| Требуйте минимальный воспроизводимый пример | Снижает галлюцинации и делает ответ проверяемым |
Перепроверяйте код через cargo clippy -- -D warnings |
Clippy ловит неидиоматичные паттерны, которые модель иногда допускает |
Используйте cargo check перед полным cargo build |
Экономит время в цикле правок |
Полезные источники, на которые стоит опираться параллельно с ИИ:
- Официальная книга «The Rust Programming Language» — doc.rust-lang.org/book
- Rust by Example — doc.rust-lang.org/rust-by-example
- Документация Tokio — tokio.rs/tokio/tutorial
- Rust API Guidelines — rust-lang.github.io/api-guidelines
- Rustonomicon (для unsafe) — doc.rust-lang.org/nomicon
Что в итоге
Rust остаётся языком с высоким порогом входа — и это не баг, а фича. Borrow checker и строгая типизация отсекают целые классы багов ещё на этапе компиляции, и именно за это Rust любят в инфраструктурных командах. Но учить его в одиночку, гугля каждую ошибку по 40 минут, — это прошлый век. Современная LLM-модель работает как терпеливый ментор, который не устаёт объяснять разницу между & и &mut в десятый раз.
Возьмите промты из этой подборки, адаптируйте под свой стиль и задачи. Начните с первого — разбора ошибки компилятора. Через пару недель вы поймаете себя на том, что уже предугадываете, что скажет borrow checker, ещё до запуска cargo build. Это и есть момент, когда Rust перестаёт быть болью и становится инструментом.
Комментарии