Работа с YAML в Rust: библиотека serde_yaml

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение в работу с 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(())
}

Не забудьте обработать возможные ошибки: отсутствие файла

Рекомендаций