Введение в работу с YAML на Rust
YAML (YAML Ain't Markup Language) — один из самых популярных форматов для конфигурационных файлов благодаря своей читаемости и гибкости. В экосистеме Rust для работы с YAML используется крейт serde_yaml, который построен на мощной платформе сериализации Serde. Эта библиотека позволяет легко преобразовывать Rust-структуры в YAML и обратно.
Установка и настройка
Добавьте зависимости в ваш Cargo.toml:
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.9"
После обновления проекта выполните cargo build. Библиотека готова к использованию.
Базовая десериализация YAML в Rust
Рассмотрим простейший пример — чтение YAML-строки и преобразование её в структуру Rust.
Шаг 1: Определяем структуру
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct Config {
host: String,
port: u16,
debug: bool,
}
Шаг 2: Парсим YAML
use serde_yaml;
fn main() -> Result<(), serde_yaml::Error> {
let yaml_str = r#"
host: "127.0.0.1"
port: 8080
debug: true
"#;
let config: Config = serde_yaml::from_str(yaml_str)?;
println!("{:?}", config);
Ok(())
}
Вывод: Config { host: "127.0.0.1", port: 8080, debug: true }
Сериализация Rust-структур в YAML
Обратный процесс — преобразование данных Rust в YAML-строку:
use serde::Serialize;
#[derive(Debug, Serialize)]
struct Server {
name: String,
ip: String,
ports: Vec<u16>,
}
fn main() -> Result<(), serde_yaml::Error> {
let server = Server {
name: "web-server".into(),
ip: "10.0.0.1".into(),
ports: vec![80, 443, 22],
};
let yaml = serde_yaml::to_string(&server)?;
println!("{}", yaml);
Ok(())
}
Результат:
---
name: web-server
ip: 10.0.0.1
ports:
- 80
- 443
- 22
Работа с вложенными структурами и тегами
YAML поддерживает сложные иерархии. В Rust это отображается на вложенные структуры и перечисления:
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct Database {
#[serde(rename = "db")]
database: DbConfig,
pool_size: u32,
}
#[derive(Debug, Serialize, Deserialize)]
struct DbConfig {
host: String,
port: u16,
user: String,
password: String,
}
fn main() -> Result<(), serde_yaml::Error> {
let yaml = r#"
db:
host: "localhost"
port: 5432
user: "admin"
password: "secret"
pool_size: 10
"#;
let db: Database = serde_yaml::from_str(yaml)?;
println!("{:#?}", db);
Ok(())
}
Обратите внимание на атрибут #[serde(rename = "db")] — он позволяет сопоставить поле Rust с другим именем в YAML.
Обработка ошибок и необязательных полей
Реальные YAML-файлы часто содержат необязательные параметры. Используйте Option<T>:
#[derive(Debug, Deserialize)]
struct AppConfig {
name: String,
version: String,
#[serde(default)]
features: Vec<String>,
#[serde(default = "default_timeout")]
timeout: u64,
}
fn default_timeout() -> u64 {
30
}
Атрибут #[serde(default)] присваивает значение по умолчанию (пустой вектор для Vec, None для Option). Можно указать функцию-генератор, как в примере с timeout.
Чтение YAML из файла
На практике конфигурации хранятся в файлах. Чтение выполняется просто:
use std::fs;
use serde_yaml;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let content = fs::read_to_string("config.yaml")?;
let config: Config = serde_yaml::from_str(&content)?;
println!("Loaded config: {:#?}", config);
Ok(())
}
Не забудьте обработать возможные ошибки: отсутствие файла