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

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

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

Начать курс

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

YAML (YAML Ain't Markup Language) — один из самых популярных форматов сериализации данных, широко используемый для конфигурационных файлов, описания структур данных и обмена информацией между сервисами. В экосистеме Rust для работы с YAML существует зрелая и производительная библиотека serde_yaml, построенная на базе мощного фреймворка сериализации serde. В этой статье мы подробно разберём, как парсить, создавать и обрабатывать YAML-документы на Rust.

Установка и подключение

Для начала работы добавьте следующие зависимости в ваш Cargo.toml:

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.9"

Библиотека serde_yaml автоматически подтянет все необходимые компоненты для парсинга и генерации YAML.

Базовая десериализация (YAML → структура Rust)

Самый распространённый сценарий — чтение конфигурационного файла. Определим структуру, которая соответствует YAML-документу:

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct Config {
    server: ServerConfig,
    database: DatabaseConfig,
}

#[derive(Debug, Deserialize)]
struct ServerConfig {
    host: String,
    port: u16,
    workers: Option<u32>,
}

#[derive(Debug, Deserialize)]
struct DatabaseConfig {
    url: String,
    pool_size: u32,
}

Теперь прочитаем YAML-файл и преобразуем его в структуру:

use std::fs;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let yaml_content = fs::read_to_string("config.yaml")?;
    let config: Config = serde_yaml::from_str(&yaml_content)?;
    
    println!("Сервер: {}:{}", config.server.host, config.server.port);
    println!("База данных: {}", config.database.url);
    
    Ok(())
}

Пример файла config.yaml:

server:
  host: "127.0.0.1"
  port: 8080
  workers: 4
database:
  url: "postgres://user:pass@localhost/db"
  pool_size: 10

Сериализация (структура Rust → YAML)

Обратный процесс — создание YAML-документа из данных в памяти:

use serde::Serialize;

#[derive(Debug, Serialize)]
struct Project {
    name: String,
    version: String,
    dependencies: Vec<Dependency>,
}

#[derive(Debug, Serialize)]
struct Dependency {
    name: String,
    version: String,
    optional: bool,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let project = Project {
        name: "my-app".to_string(),
        version: "0.1.0".to_string(),
        dependencies: vec![
            Dependency {
                name: "serde".to_string(),
                version: "1.0".to_string(),
                optional: false,
            },
            Dependency {
                name: "tokio".to_string(),
                version: "1.35".to_string(),
                optional: true,
            },
        ],
    };

    let yaml_string = serde_yaml::to_string(&project)?;
    println!("{}", yaml_string);
    
    // Сохраняем в файл
    std::fs::write("project.yaml", &yaml_string)?;
    
    Ok(())
}

Работа с произвольными YAML-документами

Иногда структура YAML заранее неизвестна. В таких случаях используйте serde_yaml::Value:

use serde_yaml::Value;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let yaml_data = "
apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app: web
spec:
  ports:
    - port: 80
      targetPort: 8080
  selector:
    app: web
";
    
    let value: Value = serde_yaml::from_str(yaml_data)?;
    
    // Доступ к вложенным данным
    if let Some(ports) = value["spec"]["ports"].as_sequence() {
        for port in ports {
            if let Some(port_num) = port["port"].as_u64() {
                println!("Порт: {}", port_num);
            }
        }
    }
    
    // Изменение значения
    if let Some(metadata) = value.get_mut("metadata") {
        if let Some(labels) = metadata.get_mut("labels") {
            labels.as_mapping_mut()
                .unwrap()
                .insert(Value::String("env".into()), Value::String("production".into()));
        }
    }
    
    println!("{}", serde_yaml::to_string(&value)?);
    
    Ok(())
}

Обработка ошибок и лучшие практики

  • Типизированные ошибки: используйте serde_yaml::Error для точного определения проблемы при парсин

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