Промты для Rust и Go: 12 сценариев от конкурентности и памяти до высоконагруженных микросервисов
В 2026 году Rust и Go перестали быть «просто языками» — это два полюса современной backend-разработки. Go остаётся языком сетевых сервисов и оркестрации, Rust — языком предсказуемой производительности и безопасной работы с памятью. Но есть нюанс: LLM-ассистенты (Claude, GPT, Gemini, локальные модели) по-разному ошибаются на этих языках. Go они пишут «уверенно», но часто забывают про утечки горутин и context cancellation. Rust они пишут синтаксически корректно, но регулярно проигрывают borrow checker — и предлагают clone() там, где нужен lifetime.
Эта подборка — не «магические фразы». Это 12 рабочих сценариев, которые я собрал из практики: как формулировать задачи так, чтобы ассистент выдавал компилируемый код, а не учебник. Каждый промт — это структура «роль → контекст → ограничения → формат вывода». Без этого LLM почти всегда скатывается в общие рассуждения.
Ниже — три уровня: базовые промты (быстрый старт), продвинутые (конкурентность, память, profiling) и экспертные (микросервисы, observability, миграции). Все примеры кода — настоящие, проверяемые на стабильных версиях инструментов. Версии и стандарты указаны как ориентир, а не как «истина последней инстанции» — проверяйте актуальность на момент чтения.
Базовые промты: быстро получить рабочий каркас
1. Идиоматичный HTTP-сервер на Go без «учебникового» мусора
Задача: сгенерировать минимальный, но production-ready HTTP-сервис на Go, который можно сразу запустить и расширять.
Промт:
Ты — senior Go-разработчик. Сгенерируй HTTP-сервис на Go 1.22+.
Требования:
- Используй net/http и стандартный ServeMux с method patterns (/items/{id}).
- Добавь graceful shutdown через signal.NotifyContext и http.Server.Shutdown.
- Логирование через log/slog (JSON handler).
- Не используй сторонние фреймворки, если можно обойтись stdlib.
- Верни: один файл main.go, комментарии только там, где неочевидно.
Формат: код + краткое объяснение 5 строк, почему именно так.
Пример результата (фрагмент):
func main() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
mux := http.NewServeMux()
mux.HandleFunc("GET /items/{id}", func(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
slog.Info("get item", "id", id)
w.Write([]byte(id))
})
srv := &http.Server{Addr: ":8080", Handler: mux}
go func() { _ = srv.ListenAndServe() }()
<-ctx.Done()
_ = srv.Shutdown(context.Background())
}
Почему работает: промт задаёт версию языка (1.22+), конкретный API (PathValue), и запрещает фреймворки. Без этого LLM часто тянет gorilla/mux или gin, хотя задача решается stdlib.
2. Rust: CLI-утилита с clap и обработкой ошибок
Задача: получить CLI на Rust с аргументами и корректной обработкой ошибок без unwrap() в production-коде.
Промт:
Ты — Rust-разработчик уровня middle+. Сгенерируй CLI на Rust (edition 2021).
Требования:
- clap с derive API, один обязательный аргумент --input.
- Ошибки через thiserror или anyhow; никаких unwrap() в main.
- Функция main возвращает Result<(), Box<dyn Error>>.
- Верни Cargo.toml и src/main.rs.
Пример результата:
use clap::Parser;
#[derive(Parser)]
struct Cli {
#[arg(long)]
input: String,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let cli = Cli::parse();
let data = std::fs::read_to_string(&cli.input)?;
println!("lines: {}", data.lines().count());
Ok(())
}
Почему работает: явное требование «никаких unwrap() в main» — ключевое. LLM по умолчанию любит unwrap(), потому что он короче.
3. Сравнение производительности: где LLM врёт
Задача: заставить ассистента честно объяснить, что нельзя сравнивать «Rust vs Go» без контекста.
Промт:
Объясни разницу Rust и Go для backend-сервиса (RPS, latency, потребление памяти).
Ограничения:
- Не давай выдуманных процентов и бенчмарков.
- Ссылайся на методологию: что именно измеряем, на каком железе, с каким профилем нагрузки.
- Приведи 3 сценария, где каждый язык выигрывает, и 2 — где проигрывает.
Формат: таблица + 5 строк вывода.
Пример результата:
| Сценарий | Go | Rust |
|---|---|---|
| Много коротких HTTP-запросов | Проще масштабировать команду, GC предсказуем при тюнинге | Нет GC-пауз, но сложнее код |
| Парсинг бинарных протоколов | OK, но аллокации заметны | Лучше контроль памяти |
| Быстрая разработка MVP | Быстрее выход | Дольше из-за borrow checker |
Почему работает: запрет на выдуманные цифры — обязателен. LLM без ограничений почти всегда придумает «на 40% быстрее».
Продвинутые промты: конкурентность, память, profiling
4. Go: утечки горутин и context cancellation
Задача: найти и исправить утечки горутин в существующем коде.
Промт:
Ты — Go-инженер, специализируешься на конкурентности.
Вот код: <вставь код>.
Найди:
1) горутины, которые могут не завершиться;
2) места, где context не пробрасывается;
3) каналы без закрытия.
Верни: список проблем, патч (diff), и как проверить через go test -race и runtime.NumGoroutine().
Пример результата:
// Было: горутина навсегда, если ctx отменён до отправки
func worker(ctx context.Context, ch chan int) {
go func() {
ch <- compute()
}()
}
// Стало:
func worker(ctx context.Context, ch chan int) {
go func() {
select {
case ch <- compute():
case <-ctx.Done():
}
}()
}
Почему работает: промт задаёт конкретные проверки (-race, NumGoroutine) — это заставляет ассистента не «рассказывать теорию», а давать инструментальную диагностику.
5. Rust: борьба с borrow checker без clone()
Задача: переписать код так, чтобы убрать лишние clone().
Промт:
Ты — Rust-эксперт по lifetimes.
Код: <вставь>.
Убери все clone(), которые не нужны. Если clone нужен — объясни почему.
Покажи изменения через diff и добавь комментарий с lifetime-аннотацией там, где это неочевидно.
Пример результата:
// Было:
fn longest(items: &Vec<String>) -> String {
items.iter().max_by_key(
|s| s.len()).unwrap().clone()
}
// Стало:
fn longest(items: &[String]) -> &str {
items.iter().max_by_key(
|s| s.len()).map(|s| s.as_str()).unwrap_or("")
}
Почему работает: требование «объясни, если clone нужен» отсекает автоматические патчи, которые просто меняют String на &str без анализа владения.
6. Профилирование: pprof (Go) и perf/flamegraph (Rust)
Задача: получить план профилирования, а не абстрактные советы.
Промт:
Составь чек-лист профилирования для Go-сервиса и Rust-сервиса.
Для Go: net/http/pprof, go tool pprof, benchstat.
Для Rust: cargo flamegraph, perf, criterion.
Формат: таблица «шаг → команда → что смотреть».
Не выдумывай флаги, которых нет.
Пример результата:
| Шаг | Go | Rust |
|---|---|---|
| CPU | go tool pprof http://localhost:6060/debug/pprof/profile |
cargo flamegraph |
| Heap | go tool pprof .../heap |
valgrind --tool=massif / heaptrack |
| Бенчмарки | go test -bench=. -benchmem |
cargo bench (criterion) |
Почему работает: промт фиксирует конкретные инструменты — LLM не сможет «изобрести» несуществующий флаг.
7. Память: аллокации и escape analysis
Задача: уменьшить аллокации в hot path.
Промт:
Проанализируй Go-код на аллокации: <вставь>.
Покажи вывод go build -gcflags="-m" для ключевых функций.
Предложи изменения: sync.Pool, предвыделение слайсов, передача по указателю.
Для Rust: покажи, где Box/Vec можно заменить на стек или SmallVec.
Пример результата:
// Было:
for _, s := range items {
buf = append(buf, []byte(s)...)
}
// Стало (предвыделение):
buf := make([]byte, 0, totalLen)
Почему работает: явное упоминание -gcflags="-m" — реальный флаг компилятора Go, который показывает escape analysis.
Экспертные промты: микросервисы, observability, миграции
8. gRPC-микросервис: Go + Rust вместе
Задача: описать схему взаимодействия сервисов на двух языках.
Промт:
Спроектируй микросервис: Go — API gateway, Rust — вычислительное ядро.
Протокол: gRPC (protobuf).
Опиши:
- .proto файл;
- как генерировать код (protoc, tonic для Rust, protoc-gen-go для Go);
- обработку таймаутов и retry.
Формат: код .proto + команды генерации.
Пример результата:
syntax = "proto3";
package compute.v1;
service Compute {
rpc Process (ProcessRequest) returns (ProcessResponse);
}
message ProcessRequest { bytes payload = 1; }
message ProcessResponse { bytes result = 1; }
Почему работает: указание конкретных инструментов (tonic, protoc-gen-go) убирает «полёт фантазии» и даёт воспроизводимые команды.
9. Observability: OpenTelemetry для Go и Rust
Задача: добавить трассировку без vendor lock-in.
Промт:
Добавь OpenTelemetry tracing в Go-сервис и Rust-сервис.
Go: go.opentelemetry.io/otel, OTLP exporter.
Rust: opentelemetry + tracing-opentelemetry.
Покажи инициализацию и пример span.
Не используй устаревшие API (opentracing).
Пример результата (Go):
ctx, span := tracer.Start(ctx, "process")
defer span.End()
Почему работает: запрет на устаревшие API (opentracing) критичен — LLM часто путает поколения библиотек.
10. Нагрузочное тестирование: k6 и wrk
Задача: сравнить поведение сервисов под нагрузкой.
Промт:
Напиши сценарий нагрузочного теста k6 для HTTP-сервиса.
Проверь: RPS, p95 latency, error rate.
Покажи, как сравнить Go и Rust сервисы на одинаковом профиле.
Не выдумывай цифры — только методология.
Пример результата:
import http from 'k6/http';
import { check } from 'k6';
export const options = { vus: 50, duration: '30s' };
export default function () {
const res = http.get('http://localhost:8080/items/1');
check(res, { 'status is 200': (r) => r.status === 200 });
}
Почему работает: k6 — реальный инструмент с настоящим JS API. Промт запрещает выдуманные метрики.
11. Миграция с Go на Rust (или наоборот) — без иллюзий
Задача: получить честный план миграции.
Промт:
Составь план миграции сервиса с Go на Rust (или наоборот).
Разбей на фазы: инвентаризация, прототип, параллельный запуск, cutover.
Укажи риски: конкурентность, FFI, найм, tooling.
Формат: таблица «фаза → цель → метрика успеха».
Пример результата:
| Фаза | Цель | Метрика |
|---|---|---|
| Инвентаризация | Найти hot path | % времени в 20% функций |
| Прототип | Проверить FFI | Работает ли cgo/FFI |
| Параллельный запуск | Сравнить | p95 latency, error rate |
| Cutover | Переключить | Rollback-план |
Почему работает: структура «фаза → метрика» превращает расплывчатый совет в проверяемый план.
12. Безопасность: аудит unsafe-кода и cgo
Задача: найти потенциально опасные места в Rust/Go коде.
Промт:
Проведи аудит кода на Rust и Go.
Ищи: unsafe-блоки, cgo, гонки данных, неинициализированную память.
Для каждой находки: строка, риск, как исправить.
Ссылайся на реальные инструменты: miri, cargo-audit, go vet, staticcheck, race detector.
Пример результата:
# Rust
cargo miri test
cargo audit
# Go
go vet ./...
staticcheck ./...
go test -race ./...
Почему работает: перечислены настоящие инструменты — LLM не сможет подсунуть выдуманный «rust-safety-checker».
Как формулировать промты, чтобы они не ломались
Есть три правила, которые я вывел из практики:
- Версия и инструмент. «Go 1.22+» и «edition 2021» — не мелочь. Без версии LLM смешивает API разных лет.
- Запрет на выдуманные цифры. Формулировка «не давай процентов без источника» резко повышает качество.
- Формат вывода. Diff, таблица, команды — это не украшение, а способ проверить, что ассистент реально понял задачу.
| Уровень | Когда использовать | Риск |
|---|---|---|
| Базовый | Каркас, CLI, HTTP | Слишком общий код |
| Продвинутый | Конкурентность, память, profiling | Неверные флаги |
| Экспертный | Микросервисы, миграции | Выдуманные API |
Что дальше
Rust и Go в 2026-м — это не «или-или», а «и-и»: Go для orchestration и сетевых сервисов, Rust для compute-ядра и мест, где важна предсказуемость памяти. Промты — это не замена инженерному мышлению, а способ быстрее получить черновик, который потом нужно проверить компилятором, race detector и профилировщиком.
Если хотите глубже — попробуйте взять один из промтов выше и прогнать его на своём коде. Скорее всего, первый ответ будет «слишком общим». Это нормально: добавьте версию, ограничения и формат — и качество вырастет. А если нужна системная база по backend-разработке, AI и инженерии — заглядывайте в материалы на asibiont.com/blog: мы разбираем не «магию», а воспроизводимые подходы.
Комментарии