Валидация данных в Rust: обзор библиотек и практических подходов
В современной разработке на Rust, особенно при создании веб-сервисов и API, валидация входящих данных — критически важная задача. Язык Rust уже предоставляет мощную систему типов, которая отсекает целые классы ошибок на этапе компиляции. Однако для проверки бизнес-логики (например, минимальная длина пароля, корректность email, диапазон чисел) нужны дополнительные инструменты. В этой статье мы рассмотрим три наиболее популярные библиотеки для валидации данных в Rust: validator, garde и valitron.
1. Библиотека validator
validator — это, пожалуй, самая известная и зрелая библиотека для валидации структур в Rust. Она построена на атрибутах (derive macros) и поддерживает множество встроенных правил.
Установка:
[dependencies]
validator = { version = "0.18", features = ["derive"] }
Пример использования:
use validator::Validate;
#[derive(Debug, Validate)]
struct User {
#[validate(length(min = 3, max = 20))]
username: String,
#[validate(email)]
email: String,
#[validate(range(min = 18, max = 120))]
age: u32,
#[validate(required)]
password: Option<String>,
}
fn main() {
let user = User {
username: "Jo".to_string(),
email: "invalid-email".to_string(),
age: 15,
password: None,
};
match user.validate() {
Ok(_) => println!("Валидация пройдена!"),
Err(errors) => {
for (field, err) in &errors.errors() {
println!("Ошибка в поле '{}': {:?}", field, err);
}
}
}
}
Преимущества:
- Большое количество встроенных валидаторов: email, URL, длина, диапазон, регулярные выражения.
- Поддержка кастомных функций валидации через атрибут
custom. - Хорошая интеграция с веб-фреймворками (Actix-web, Axum, Rocket).
Недостатки:
- Зависит от макросов, что может замедлить компиляцию.
- Ошибки возвращаются в виде
HashMap, что требует дополнительной обработки для API.
2. Библиотека garde
garde — более современная и быстрая альтернатива validator. Она также использует derive-макросы, но предлагает более гибкий API и лучшую производительность.
Установка:
[dependencies]
garde = "0.20"
Пример использования:
use garde::Validate;
#[derive(Debug, Validate)]
struct LoginRequest {
#[garde(length(min = 3, max = 50))]
username: String,
#[garde(ascii, length(min = 8))]
password: String,
#[garde(skip)]
remember_me: bool,
}
fn main() {
let req = LoginRequest {
username: "ab".to_string(),
password: "1234567".to_string(),
remember_me: true,
};
if let Err(errors) = req.validate() {
for error in &errors {
println!("Ошибка: {} - {}", error.path(), error.message());
}
} else {
println!("Валидация успешна!");
}
}
Преимущества:
- Высокая производительность за счёт оптимизированного кода.
- Чёткие сообщения об ошибках с путём к полю.
- Поддержка вложенных структур и коллекций.
- Возможность создавать свои правила без макросов.
Недостатки:
- Меньше встроенных валидаторов (нет email, URL по умолчанию).
- Сообщество меньше, чем у
validator.
3. Библиотека valitron
valitron — это лёгкая библиотека, которая не использует макросы. Вместо этого она предлагает fluent-интерфейс для построения правил валидации. Это может быть удобно, когда структура данных динамическая или неизвестна на этапе компиляции.
Установка:
[dependencies]
valitron = "0.5"
Пример использования:
use valitron::{Validator, Rule};
fn main() {
let data = serde_json::json!({
"name": "A",
"email": "bad",
"age": 17
});
let validator = Validator::new()
.rule("name", Rule::required().length(3..=50))
.rule("email", Rule::required().email())