10 промтов для Rust: системное программирование, CLI и WebAssembly
Rust — это язык, который завоевал сердца разработчиков благодаря сочетанию производительности C++ и безопасности памяти. С момента выхода Rust 1.0 в 2015 году (официальный релиз состоялся 15 мая 2015 года, согласно The Rust Programming Language Book), экосистема выросла до более чем 100 000 крейтов на crates.io (данные на июль 2026 года). Однако написание эффективного кода на Rust требует не только знания синтаксиса, но и понимания идиом, unsafe-блоков, работы с памятью и FFI. В этой статье мы собрали 10 промтов — от базовых до экспертных, — которые помогут вам писать безопасные CLI-утилиты, оптимизировать системный код и создавать WebAssembly-модули. Каждый промт сопровождается задачей, формулировкой и примером результата.
Категория 1: Базовые промты для безопасного кода
Промт 1: Безопасная работа с указателями через Box и Rc
Задача: Разработчику нужно передать владение динамической структурой данных (например, связным списком) без утечек памяти.
Промт: Напиши идиоматичный код на Rust для односвязного списка, используя Box для владения узлами. Добавь метод push и pop. Избегай unsafe-блоков.
Пример результата:
#[derive(Debug)]
struct Node<T> {
value: T,
next: Option<Box<Node<T>>>,
}
impl<T> Node<T> {
fn new(value: T) -> Self {
Node { value, next: None }
}
}
struct LinkedList<T> {
head: Option<Box<Node<T>>>,
}
impl<T> LinkedList<T> {
fn new() -> Self {
LinkedList { head: None }
}
fn push(&mut self, value: T) {
let new_node = Box::new(Node::new(value));
match self.head.take() {
Some(old_head) => new_node.next = Some(old_head),
None => {}
}
self.head = Some(new_node);
}
fn pop(&mut self) -> Option<T> {
self.head.take().map(
|node| {
self.head = node.next;
node.value
})
}
}
fn main() {
let mut list = LinkedList::new();
list.push(1);
list.push(2);
println!("{:?}", list.pop()); // Some(2)
}
Промт 2: Обработка ошибок с Result и ?
Задача: Парсинг числа из строки с обработкой ошибки в стиле Rust.
Промт: Напиши функцию, которая принимает строку и возвращает Result<i32, String>. Используй оператор ? для раннего возврата.
Пример результата:
fn parse_number(input: &str) -> Result<i32, String> {
let num: i32 = input.trim().parse().map_err(
|e| format!("Parse error: {}", e))?;
Ok(num)
}
fn main() {
match parse_number(" 42 ") {
Ok(n) => println!("Parsed: {}", n),
Err(e) => println!("Error: {}", e),
}
}
Промт 3: Использование match для enum
Задача: Обработка разных вариантов enum без потери производительности.
Промт: Создай enum Command с вариантами Quit, Move { x: i32, y: i32 }, Write(String). Напиши функцию, которая принимает Command и выполняет соответствующее действие, используя match.
Пример результата:
enum Command {
Quit,
Move { x: i32, y: i32 },
Write(String),
}
fn handle_command(cmd: Command) {
match cmd {
Command::Quit => println!("Exiting..."),
Command::Move { x, y } => println!("Moving to ({}, {})", x, y),
Command::Write(text) => println!("Writing: {}", text),
}
}
fn main() {
handle_command(Command::Move { x: 10, y: 20 });
}
Категория 2: Продвинутые промты для CLI и парсинга
Промт 4: Создание CLI-утилиты с clap
Задача: Разработать консольную утилиту для подсчёта строк в файле с поддержкой флагов.
Промт: Используя крейт clap (версия 4.x, доступен на crates.io), напиши CLI-приложение, которое принимает имя файла и флаг --verbose. Выведи количество строк и, если verbose, дополнительную статистику.
Пример результата:
use clap::Parser;
use std::fs::File;
use std::io::{BufRead, BufReader};
#[derive(Parser)]
#[command(name = "linecount")]
struct Args {
/// Input file path
file: String,
/// Enable verbose output
#[arg(short, long)]
verbose: bool,
}
fn main() {
let args = Args::parse();
let file = File::open(&args.file).expect("Cannot open file");
let reader = BufReader::new(file);
let lines: Vec<String> = reader.lines().filter_map(
|l| l.ok()).collect();
println!("Lines: {}", lines.len());
if args.verbose {
let total_chars: usize = lines.iter().map(
|l| l.len()).sum();
println!("Total characters: {}", total_chars);
}
}
Промт 5: Парсинг JSON с serde
Задача: Разобрать JSON-ответ от API (например, погода) в структуру Rust.
Промт: Используя крейты serde и serde_json, определи структуру Weather с полями temperature (f64) и humidity (f64). Напиши функцию, которая принимает строку JSON и возвращает Result<Weather, serde_json::Error>.
Пример результата:
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct Weather {
temperature: f64,
humidity: f64,
}
fn parse_weather(json: &str) -> Result<Weather, serde_json::Error> {
serde_json::from_str(json)
}
fn main() {
let data = r#"{"temperature": 23.5, "humidity": 60.0}"#;
match parse_weather(data) {
Ok(w) => println!("{:?}", w),
Err(e) => println!("Error: {}", e),
}
}
Промт 6: Работа с потоками через каналы
Задача: Реализовать producer-consumer с помощью std::sync::mpsc.
Промт: Напиши программу, где один поток генерирует числа от 1 до 10, а второй поток выводит их квадраты. Используй канал mpsc.
Пример результата:
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
let producer = thread::spawn(move || {
for i in 1..=10 {
tx.send(i).unwrap();
}
});
let consumer = thread::spawn(move || {
for received in rx {
println!("Square of {} is {}", received, received * received);
}
});
producer.join().unwrap();
consumer.join().unwrap();
}
Категория 3: Экспертные промты для системного программирования и WebAssembly
Промт 7: Unsafe-блоки для ручного управления памятью
Задача: Реализовать простой аллокатор памяти, используя unsafe для работы с сырыми указателями.
Промт: Напиши код, который выделяет блок памяти размером 1024 байта через std::alloc::alloc, записывает туда число 42 как *mut i32, читает его и освобождает память. Используй unsafe только там, где это необходимо.
Пример результата:
use std::alloc::{alloc, dealloc, Layout};
fn main() {
unsafe {
let layout = Layout::from_size_align(1024, 4).unwrap();
let ptr = alloc(layout);
if ptr.is_null() {
panic!("Allocation failed");
}
*(ptr as *mut i32) = 42;
println!("Value: {}", *(ptr as *mut i32));
dealloc(ptr, layout);
}
}
Промт 8: FFI — вызов C из Rust
Задача: Связать Rust-код с библиотекой C (например, libm для математических функций).
Промт: Используя extern "C", объяви функцию sqrt из libc. Напиши Rust-функцию, которая вычисляет квадратный корень числа типа f64 и выводит результат.
Пример результата:
extern "C" {
fn sqrt(x: f64) -> f64;
}
fn main() {
let x = 25.0;
unsafe {
println!("Square root of {} is {}", x, sqrt(x));
}
}
Промт 9: Сборка WebAssembly модуля
Задача: Создать WASM-модуль, который складывает два числа, и экспортировать функцию.
Промт: Напиши Rust-код с атрибутом #[no_mangle] и extern "C", который экспортирует функцию add(a: i32, b: i32) -> i32. Скомпилируй в .wasm через wasm-pack.
Пример результата:
#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 {
a + b
}
Команда сборки: wasm-pack build --target web.
Промт 10: Взаимодействие с JavaScript из WASM
Задача: Вызвать JavaScript-функцию из Rust-модуля через wasm-bindgen.
Промт: Используя крейт wasm-bindgen, напиши код, который вызывает alert из JS для отображения сообщения.
Пример результата:
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
fn alert(s: &str);
}
#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}
Заключение
Эти 10 промтов охватывают ключевые аспекты Rust: от безопасного управления памятью и обработки ошибок до системного программирования с unsafe и WebAssembly. Практика с ними поможет вам освоить идиомы языка и подготовиться к реальным проектам. Для углублённого изучения рекомендую официальную документацию (doc.rust-lang.org) и книгу «The Rust Programming Language» (доступна бесплатно на rust-lang.org).
Комментарии