Перейти к основному содержимому

Простые приложения на Rust

Разработчику Архитектору

Простые приложения на Rust

Rust подходит для надёжных утилит: компилятор ловит ошибки владения до запуска, cargo собирает бинарник и тесты. Примеры ниже — на стандартной библиотеке; для HTTP в продакшене чаще берут Axum, но для старта достаточно понять Result, match и работу с файлами.

Порядок: после первой программы и типов с владением.

Как запускать примеры из главы

  1. Для примеров с rand / serde: cargo new demo && cd demo, вставьте код в src/main.rs, добавьте зависимости в Cargo.toml.
  2. Команда: cargo run
  3. Один файл без 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 символов.
use rand::Rng;
use rand::seq::SliceRandom;

fn generate_password(len: usize) -> String {
const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%";
let mut rng = rand::thread_rng();
(0..len)
.map(|_| CHARSET[rng.gen_range(0..CHARSET.len())] as char)
.collect()
}

fn main() {
println!("{}", generate_password(16));
}

В Cargo.toml: rand = "0.8". Для паролей в продакшене предпочтительнее rand с OsRng / crate getrandom.


Сортировщик текстового файла

Как запустить

  • Файл: src/main.rs в проекте cargo new sort_file
  • Подготовка: input.txt в корне проекта; путь в sort_file(...).
  • Команда: cargo run
  • Результат: output.txt с отсортированными строками.
use std::fs;
use std::io;

fn sort_file(input: &str, output: &str) -> io::Result<()> {
let mut lines: Vec<String> = fs::read_to_string(input)?
.lines()
.map(str::trim)
.filter(|s| !s.is_empty())
.map(String::from)
.collect();
lines.sort();
fs::write(output, lines.join("\n") + "\n")?;
Ok(())
}

Разбор: ? пробрасывает io::Error; владение строк переходит в Vec без лишних копий там, где достаточно &str при чтении.


Консольный калькулятор

Как запустить

  • Команда: cargo run (проект с этим main)
  • Нужно: терминал; ввод вида 10 + 3 или по строкам — как в коде.
  • Результат: = 13.0 или сообщение об ошибке.
use std::io::{self, Write};

fn calc(a: f64, b: f64, op: char) -> Result<f64, String> {
Ok(match op {
'+' => a + b,
'-' => a - b,
'*' => a * b,
'/' if b == 0.0 => return Err("деление на ноль".into()),
'/' => a / b,
_ => return Err(format!("неизвестный оператор: {op}")),
})
}

fn main() -> io::Result<()> {
let stdin = io::stdin();
loop {
print!("a op b (q): ");
io::stdout().flush()?;
let mut line = String::new();
stdin.read_line(&mut line)?;
let line = line.trim();
if line == "q" { break; }
let mut parts = line.split_whitespace();
let a: f64 = parts.next().ok_or("a?")?.parse().map_err(|_| "a?")?;
let op = parts.next().ok_or("op?")?.chars().next().ok_or("op?")?;
let b: f64 = parts.next().ok_or("b?")?.parse().map_err(|_| "b?")?;
match calc(a, b, op) {
Ok(v) => println!("= {v}"),
Err(e) => eprintln!("{e}"),
}
}
Ok(())
}

Трекер задач в JSON (serde)

Как запустить

  • Cargo.toml: serde = { version = "1", features = ["derive"] }, serde_json = "1"
  • Команда: cargo run
  • Результат: файл tasks.json в каталоге проекта.
use serde::{Deserialize, Serialize};
use std::fs;

#[derive(Debug, Serialize, Deserialize)]
struct Task {
id: u64,
title: String,
done: bool,
}

const DB: &str = "tasks.json";

fn load() -> Vec<Task> {
fs::read_to_string(DB)
.ok()
.and_then(|s| serde_json::from_str(&s).ok())
.unwrap_or_default()
}

fn save(tasks: &[Task]) -> std::io::Result<()> {
fs::write(DB, serde_json::to_string_pretty(tasks)?)
}

fn main() -> std::io::Result<()> {
let mut tasks = load();
tasks.push(Task {
id: 1,
title: "Изучить Rust".into(),
done: false,
});
save(&tasks)?;
Ok(())
}

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.
use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};

fn handle(mut stream: TcpStream) -> std::io::Result<()> {
let mut buf = [0u8; 1024];
let n = stream.read(&mut buf)?;
let req = String::from_utf8_lossy(&buf[..n]);
let body = r#"{"ok":true}"#;
let response = format!(
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\n\r\n{}",
body.len(),
body
);
stream.write_all(response.as_bytes())
}

fn main() -> std::io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:3000")?;
for stream in listener.incoming().flatten() {
let _ = handle(stream);
}
Ok(())
}

Для учебного API дальше переходите на Axum.


Обход каталога

Как запустить

  • Команда: cargo run из каталога, который нужно сканировать (измените путь в scan(...)).
  • Результат: кортеж (число_файлов, байты).
use std::fs;
use std::path::Path;

fn scan(dir: &Path, ext: &str) -> std::io::Result<(usize, u64)> {
let mut files = 0usize;
let mut bytes = 0u64;
for entry in fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
let (f, b) = scan(&path, ext)?;
files += f;
bytes += b;
} else if path.extension().and_then(|e| e.to_str()) == Some(ext.trim_start_matches('.')) {
files += 1;
bytes += entry.metadata()?.len();
}
}
Ok((files, bytes))
}

Характерный пример для Rust — Result и match

Как запустить

  • Команда: cargo run или rustc main.rs && ./main
  • Результат: для 80808080; для abc и 0 — сообщения об ошибке.
fn parse_port(s: &str) -> Result<u16, String> {
let port: u16 = s.parse().map_err(|_| "порт должен быть числом".to_string())?;
if port == 0 {
return Err("порт не может быть 0".into());
}
Ok(port)
}

fn main() {
for input in ["8080", "abc", "0"] {
match parse_port(input) {
Ok(p) => println!("{input} → {p}"),
Err(e) => println!("{input}: {e}"),
}
}
}

Ошибки — часть сигнатуры; это отличает Rust от языков с исключениями «на всё».


См. также: Cargo · ошибки · async · Axum

См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).