Промты для Rust: как прокачать владение, lifetimes и async без ментора и сэкономить месяцы практики
Rust — один из самых любимых и одновременно сложных языков. По данным опроса Stack Overflow Developer Survey 2023, Rust четвёртый год подряд признан самым любимым языком программирования. Однако порог входа остаётся высоким: borrow checker, lifetimes, асинхронность — всё это требует времени и практики. Хорошо, если рядом есть опытный наставник, который объяснит ошибки и подскажет идиоматичные решения. А если нет? На помощь приходят большие языковые модели (LLM), такие как ChatGPT, Claude, Gemini. Они могут стать вашим персональным ментором по Rust, если правильно формулировать запросы — промты.
В этой статье я собрал 15 промтов, которые превращают LLM в наставника по Rust. Каждый промт решает конкретную задачу: от разбора ошибок borrow checker до написания асинхронного кода на Tokio и перехода с C++/Go. Вы получите пример запроса и пояснение, для чего он подходит. Все промты проверены на практике и соответствуют актуальным версиям Rust (1.75+) и Tokio 1.x.
Почему LLM — это не замена, но мощный ускоритель
LLM не заменят реальный опыт, но могут значительно ускорить обучение. Они мгновенно отвечают на вопросы, генерируют примеры кода, объясняют сложные концепции на аналогиях. Главное — не слепо копировать код, а анализировать его и проверять. В этой подборке вы найдёте промты для разных уровней: базовых, продвинутых и экспертных.
Базовые промты: разбираемся с владением и заимствованием
Промт 1: Объяснение ошибки borrow checker на аналогии
Задача: Понять, почему компилятор ругается на одновременное изменяемое и неизменяемое заимствование.
Промт:
Я новичок в Rust. Объясни ошибку borrow checker на аналогии из реальной жизни. Вот код:
fn main() {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &mut s;
println!("{} {}", r1, r2);
}
Компилятор выдаёт ошибку. Почему? Приведи аналогию и покажи, как исправить.
Пример результата: LLM объяснит, что это как пытаться одновременно читать книгу и писать в неё — нельзя. Приведёт аналогию с библиотекой: пока один читает (неизменяемая ссылка), другой не может редактировать (изменяемая ссылка). Покажет исправленный код с разделением областей видимости.
Промт 2: Генерация идиоматичного кода для задачи
Задача: Написать функцию, которая принимает строку и возвращает количество слов, используя идиомы Rust.
Промт:
Напиши идиоматичную функцию на Rust, которая считает количество слов в строке. Используй итераторы и срезы. Объясни, почему выбран такой подход.
Пример результата: LLM предложит:
fn count_words(text: &str) -> usize {
text.split_whitespace().count()
}
Пояснит, что split_whitespace эффективно обрабатывает пробелы, а итераторы избегают ручных циклов.
Промт 3: Преобразование кода с C++ на Rust
Задача: Перевести простой класс с C++ на Rust, сохранив идиоматичность.
Промт:
Переведи этот код с C++ на Rust. Объясни различия в управлении памятью и владении.
class Point {
public:
double x, y;
Point(double x, double y) : x(x), y(y) {}
double distance(const Point& other) const {
return sqrt(pow(x - other.x, 2) + pow(y - other.y, 2));
}
};
Пример результата: LLM создаст структуру с реализацией методов, покажет использование &self и объяснит, что в Rust не нужен конструктор по умолчанию, а владение передаётся через ссылки.
Продвинутые промты: lifetimes, трейты, макросы
Промт 4: Разбор сложной ошибки lifetimes
Задача: Понять, почему компилятор требует аннотацию времени жизни.
Промт:
Объясни ошибку lifetimes в этом коде. Покажи, как исправить, и объясни правило элизии lifetimes.
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() { x } else { y }
}
Пример результата: LLM объяснит, что компилятор не может вывести время жизни возвращаемой ссылки, и предложит добавить аннотацию <'a>. Также напомнит правила элизии.
Промт 5: Реализация трейта для нескольких типов
Задача: Написать трейт Summary и реализовать его для String и Vec<i32>.
Промт:
Создай трейт Summary с методом summarize(&self) -> String. Реализуй его для String и Vec<i32>. Покажи пример использования.
Пример результата: LLM сгенерирует код:
trait Summary {
fn summarize(&self) -> String;
}
impl Summary for String {
fn summarize(&self) -> String {
format!("Строка длиной {}", self.len())
}
}
impl Summary for Vec<i32> {
fn summarize(&self) -> String {
format!("Вектор из {} элементов", self.len())
}
}
И покажет вызов.
Промт 6: Написание макроса для логирования
Задача: Создать макрос log_info!, который выводит сообщение с уровнем INFO.
Промт:
Напиши макрос log_info!, который принимает форматную строку и аргументы, как println!, но добавляет префикс "[INFO]". Объясни, как работают макросы в Rust.
Пример результата: LLM предложит:
macro_rules! log_info {
($($arg:tt)*) => {
println!("[INFO] {}", format!($($arg)*));
};
}
И объяснит, что $($arg:tt)* захватывает любые токены.
Промт 7: Использование умных указателей
Задача: Понять разницу между Box, Rc, Arc и RefCell.
Промт:
Объясни на примерах, когда использовать Box, Rc, Arc и RefCell. Приведи по одному примеру для каждого.
Пример результата: LLM покажет:
- Box<T> для рекурсивных типов.
- Rc<T> для множественного владения в одном потоке.
- Arc<T> для многопоточности.
- RefCell<T> для внутренней мутабельности.
Экспертные промты: async, Tokio, тестирование, оптимизация
Промт 8: Асинхронный HTTP-клиент на Tokio
Задача: Написать асинхронную функцию, которая делает GET-запрос и возвращает тело ответа.
Промт:
Напиши асинхронную функцию на Rust с Tokio и reqwest, которая делает GET-запрос по URL и возвращает String. Обработай ошибки. Покажи пример вызова.
Пример результата: LLM сгенерирует:
use reqwest;
async fn fetch_url(url: &str) -> Result<String, reqwest::Error> {
let resp = reqwest::get(url).await?;
resp.text().await
}
И покажет #[tokio::main].
Промт 9: Параллельная обработка с rayon
Задача: Ускорить обработку вектора с помощью rayon.
Промт:
Как с помощью rayon распараллелить обработку вектора? Напиши пример, который умножает каждый элемент на 2 и суммирует.
Пример результата: LLM предложит:
use rayon::prelude::*;
let sum: i32 = vec![1,2,3].par_iter().map(
|x| x * 2).sum();
И объяснит, что rayon автоматически использует пул потоков.
Промт 10: Написание юнит-тестов
Задача: Сгенерировать тесты для функции сложения.
Промт:
Напиши юнит-тесты для функции add(a: i32, b: i32) -> i32. Используй модуль tests и атрибут #[test].
Пример результата: LLM создаст:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(2, 3), 5);
}
}
Промт 11: Оптимизация кода с помощью clippy
Задача: Найти и исправить предупреждения clippy.
Промт:
Вот код. Запусти мысленно cargo clippy и предложи исправления.
fn main() {
let mut v = Vec::new();
v.push(1);
v.push(2);
let sum = v.iter().fold(0,
|acc, x| acc + x);
println!("{}", sum);
}
Пример результата: LLM укажет, что можно использовать vec![1, 2] и sum() вместо fold.
Промт 12: Переход с Go на Rust: сравнение конкурентности
Задача: Понять, как горутины и каналы映射 на async/await и каналы Tokio.
Промт:
Я знаю Go. Объясни, как в Rust реализовать конкурентность, аналогичную горутинам и каналам. Приведи пример с Tokio.
Пример результата: LLM сравнит горутины с tokio::spawn, каналы с tokio::sync::mpsc.
Промт 13: Работа с базами данных через sqlx
Задача: Написать асинхронный запрос к PostgreSQL.
Промт:
Покажи пример асинхронного запроса к PostgreSQL с sqlx. Создай таблицу users и выполни SELECT.
Пример результата: LLM сгенерирует код с sqlx::query! и пулом соединений.
Промт 14: Реализация паттерна Builder
Задача: Создать билдер для структуры Config.
Промт:
Реализуй паттерн Builder для структуры Config с полями host: String, port: u16, timeout: Duration. Покажи пример использования.
Пример результата: LLM предложит цепочку методов.
Промт 15: Отладка асинхронного дедлока
Задача: Найти причину взаимной блокировки.
Промт:
Вот асинхронный код с Tokio. Он зависает. Найди дедлок и объясни, как исправить.
use tokio::sync::Mutex;
async fn foo(m1: &Mutex<i32>, m2: &Mutex<i32>) {
let _a = m1.lock().await;
let _b = m2.lock().await;
}
async fn bar(m1: &Mutex<i32>, m2: &Mutex<i32>) {
let _b = m2.lock().await;
let _a = m1.lock().await;
}
Пример результата: LLM объяснит, что порядок захвата разный, и предложит унифицировать.
Как использовать промты эффективно
- Уточняйте контекст: добавляйте версию Rust, используемые крейты.
- Просите объяснения: не просто код, а почему так.
- Проверяйте код: запускайте
cargo check, тесты, clippy. - Итеративно улучшайте: если ответ не подходит, задавайте уточняющие вопросы.
Сравнение промтов по уровням
| Уровень | Промты | Для кого |
|---|---|---|
| Базовый | 1-3 | Новички, переход с других языков |
| Продвинутый | 4-7 | Разработчики с опытом, изучение lifetimes и трейтов |
| Экспертный | 8-15 | Асинхронность, оптимизация, тестирование, миграция |
Заключение
LLM не заменят практику, но могут стать отличным наставником, если использовать правильные промты. Экспериментируйте, адаптируйте их под свои задачи. Помните: лучший способ выучить Rust — писать код, разбирать ошибки и не бояться borrow checker. Удачи!
Источники:
- Stack Overflow Developer Survey 2023
- Официальная книга Rust
- Документация Tokio
- Rust Clippy
Комментарии