Простые приложения на Rust
Простые приложения на Rust
Rust подходит для надёжных утилит: компилятор ловит ошибки владения до запуска, cargo собирает бинарник и тесты. Примеры ниже — на стандартной библиотеке; для HTTP в продакшене чаще берут Axum, но для старта достаточно понять Result, match и работу с файлами.
Порядок: после первой программы и типов с владением.
Как запускать примеры из главы
- Для примеров с
rand/serde:cargo new demo && cd demo, вставьте код вsrc/main.rs, добавьте зависимости вCargo.toml. - Команда:
cargo run - Один файл без Cargo:
rustc src/main.rs && ./main(Linux/macOS) илиmain.exe(Windows).
Проверка: rustc --version, cargo --version.
Генератор паролей
Как запустить
- Проект:
cargo new password && cd password - Cargo.toml:
rand = "0.8" - Команда:
cargo run - Результат: одна строка — пароль из 16 символов.
Код ITЗагрузка примера кода…
Разбор:
- Функция
generate_password(len: usize) -> Stringпринимает длину и возвращает уже собранную строку пароля, поэтому контракт функции ясен по сигнатуре. - Константа
CHARSETхранится как&[u8], это удобный формат для случайного выбора индекса и последующего приведения байта кchar. rand::thread_rng()создает генератор случайных чисел, привязанный к текущему потоку выполнения.- Цепочка
(0..len).map(...).collect()строит пароль декларативно: для каждой позиции выбирается случайный символ и затем все символы собираются вString. - Внутри
gen_range(0..CHARSET.len())верхняя граница не включается, что защищает от выхода за пределы массива. - На проде такой пример обычно усиливают криптостойким источником случайности (
OsRng) и проверкой политики сложности пароля.
В Cargo.toml: rand = "0.8". Для паролей в продакшене предпочтительнее rand с OsRng / crate getrandom.
Сортировщик текстового файла
Как запустить
- Файл:
src/main.rsв проектеcargo new sort_file - Подготовка:
input.txtв корне проекта; путь вsort_file(...). - Команда:
cargo run - Результат:
output.txtс отсортированными строками.
Код ITЗагрузка примера кода…
Разбор:
sort_file(input, output) -> io::Result<()>показывает стандартный шаблон для I/O-функций, где ошибка явно возвращается наружу.fs::read_to_string(input)?считывает весь файл в память и сразу пробрасываетio::Error, если чтение не удалось.- Цепочка
.lines().map(str::trim).filter(...).map(String::from)сначала очищает строки, затем убирает пустые и только после этого материализует их вVec<String>. lines.sort()сортирует строки in-place по лексикографическому порядку, без создания второй коллекции.fs::write(...)записывает результат обратно в файл, а добавление+ "\n"сохраняет перевод строки в конце файла.fn main() -> io::Result<()>вызываетsort_fileи пробрасывает ошибки через?, поэтому пример сразу запускается черезcargo run.- Такой пример удобно расширять выбором регистра (case-sensitive/case-insensitive) и сортировкой по кастомному компаратору.
Консольный калькулятор
Как запустить
- Команда:
cargo run(проект с этимmain) - Нужно: терминал; ввод вида
10 + 3или по строкам — как в коде. - Результат:
= 13.0или сообщение об ошибке.
Код ITЗагрузка примера кода…
Разбор:
- Функция
calc(a, b, op)изолирует арифметическую бизнес-логику от кода ввода-вывода, что упрощает тестирование. - Внутри
match opкаждая операция описана отдельной веткой, аreturn Err(...)мгновенно прерывает вычисление в невалидных сценариях. - Условная ветка
'/' if b == 0.0показывает guard вmatch: оператор совпал, но дополнительно проверяется условие. - Цикл
loopвmainреализует интерактивный режим калькулятора до командыq. - Разбор ввода через
split_whitespace()иparse()делает формат команды строгим и предсказуемым. stdout().flush()?принудительно выводит приглашение перед чтением, чтобы пользователь видел prompt сразу.
Трекер задач в JSON (serde)
Как запустить
- Cargo.toml:
serde = { version = "1", features = ["derive"] },serde_json = "1" - Команда:
cargo run - Результат: файл
tasks.jsonв каталоге проекта.
Код ITЗагрузка примера кода…
Разбор:
#[derive(Serialize, Deserialize)]автоматически добавляет сериализацию и десериализацию, поэтому структуруTaskлегко сохранять в JSON и читать обратно.const DB: &str = "tasks.json"централизует имя файла-хранилища и убирает магические строки из функций.- В
load()цепочкаok().and_then(...).unwrap_or_default()преобразует ошибки чтения и парсинга в "пустой список", сохраняя простоту для учебного сценария. save(tasks — &[Task])принимает срез, а неVec, поэтому функция не зависит от конкретного контейнера и не требует владения коллекцией.serde_json::to_string_pretty(tasks)?формирует читаемый JSON, удобный для ручной проверки и ревью.- В практическом приложении к этому примеру обычно добавляют уникальные
id, обновление задач и явное логирование ошибок чтения.
Cargo.toml — serde = { version = "1", features = ["derive"] }, serde_json = "1".
Минимальный HTTP-сервер (hyper / tiny_http альтернатива — std через TcpListener)
Как запустить
- Команда:
cargo run - Проверка: в другом терминале
curl http://127.0.0.1:3000/— JSON в ответе. - Остановка:
Ctrl+C.
Код ITЗагрузка примера кода…
Разбор:
TcpListener::bind("127.0.0.1:3000")?поднимает локальный TCP-сервер и возвращает ошибку, если порт занят или недоступен.- В
for stream in listener.incoming().flatten()цикл принимает входящие соединения, пропуская ошибочные элементы без падения процесса. handle(mut stream: TcpStream)читает сырой HTTP-запрос в буфер, затем формирует ответ вручную через строку статуса и заголовки.String::from_utf8_lossyпозволяет безопасно логировать/разбирать входные байты даже при неидеальной кодировке запроса.- Заголовок
Content-Lengthвычисляется из длиныbody, чтобы клиент понимал, сколько байтов читать в ответе. - Этот код полезен как учебная модель HTTP поверх TCP, а для production обычно переходят на
axum/actix-web.
Для учебного API дальше переходите на Axum.
Обход каталога
Как запустить
- Команда:
cargo runиз каталога, который нужно сканировать (измените путь вscan(...)). - Результат: кортеж
(число_файлов, байты).
Код ITЗагрузка примера кода…
Разбор:
- Рекурсивная функция
scan(dir, ext)обходит дерево каталогов в глубину и возвращает агрегированный результат(количество_файлов, общий_размер). fs::read_dir(dir)?отдает итератор по директории, аentry?внутри цикла обрабатывает ошибки доступа к отдельным элементам.- Проверка
path.is_dir()разделяет ветки: либо рекурсивный обход подпапки, либо обработка файла. - Условие по
path.extension()фильтрует файлы по расширению, аtrim_start_matches('.')позволяет передаватьextв двух форматах. - Размер файла берется через
entry.metadata()?.len(), после чего суммируется в общий счетчикbytes. - Такой пример удобно расширить ограничением глубины обхода, исключениями по директориям и параллельным сканированием.
Характерный пример для Rust — Result и match
Как запустить
- Команда:
cargo runилиrustc main.rs && ./main - Результат: для
8080—8080; дляabcи0— сообщения об ошибке.
Код ITЗагрузка примера кода…
Разбор:
parse_portдемонстрирует идиоматичную валидацию: сначала парсинг строки в число, затем доменное ограничениеport != 0.s.parse().map_err(|_| "...".to_string())?преобразует техническую ошибку парсинга в понятное сообщение для пользователя.- Возврат
Result<u16, String>делает ошибочный путь частью API функции, а не скрытым побочным сценарием. match parse_port(input)вmainнаглядно разделяет успешный и неуспешный путь, что упрощает диагностику.- Перебор массива
["8080", "abc", "0"]показывает проверку как корректного значения, так и двух разных классов ошибок. - Этот шаблон удобно переносить в CLI и серверы, где входные параметры всегда требуют строгой валидации.
Ошибки — часть сигнатуры; это отличает Rust от языков с исключениями "на всё".
Мини-сниппет: аргументы командной строки без внешних библиотек:
use std::env;
fn main() {
let mut args = env::args().skip(1);
let input = args.next().unwrap_or_else(|| "input.txt".into());
let output = args.next().unwrap_or_else(|| "output.txt".into());
println!("{input} -> {output}");
}
Разбор:
env::args()возвращает итератор строк: первый элемент — имя программы, дальше аргументы пользователя..skip(1)убирает путь к исполняемому файлу из разбора.unwrap_or_else(|| ...)задаёт значения по умолчанию, если аргумент не передан.- Такой каркас легко расширить вызовом
sort_file(&input, &output)?из примера выше.
См. также: Cargo · ошибки · async · Axum
Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.