Работа с базами данных в Rust: PostgreSQL и MongoDB

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

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

Начать курс

Введение: Почему Rust и базы данных — идеальное сочетание

Rust — это современный системный язык программирования, который сочетает в себе производительность C/C++ с безопасностью памяти, гарантируемой компилятором. При разработке бэкенд-сервисов, веб-приложений и микросервисов практически всегда возникает необходимость взаимодействия с базами данных. В этой статье мы подробно разберём, как работать с двумя самыми популярными типами БД: реляционной PostgreSQL и документно-ориентированной MongoDB.

Мы рассмотрим подключение, выполнение CRUD-операций, транзакции и агрегации на практических примерах. Все примеры кода написаны на Rust с использованием асинхронного рантайма tokio, что даёт максимальную производительность.

1. Настройка проекта и установка зависимостей

Для начала создадим новый проект и добавим необходимые крейты. Откройте терминал и выполните:

cargo new rust_db_example
cd rust_db_example

Добавим зависимости в файл Cargo.toml:

[dependencies]
tokio = { version = "1", features = ["full"] }
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres"] }
mongodb = "2.8"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
chrono = { version = "0.4", features = ["serde"] }

Теперь структура проекта готова. Мы будем использовать sqlx для PostgreSQL — это асинхронный драйвер с поддержкой компилируемых запросов, и официальный драйвер mongodb для работы с MongoDB.

1.1. Подключение к PostgreSQL через sqlx

Напишем модуль для работы с PostgreSQL. Создадим файл src/postgres.rs:

use sqlx::postgres::PgPoolOptions;
use sqlx::PgPool;
use anyhow::Result;

pub async fn connect_to_postgres() -> Result<PgPool> {
    let database_url = "postgres://user:password@localhost:5432/mydb";
    let pool = PgPoolOptions::new()
        .max_connections(5)
        .connect(database_url)
        .await?;
    println!("✅ Подключились к PostgreSQL");
    Ok(pool)
}

// Определяем модель пользователя
#[derive(Debug, sqlx::FromRow)]
pub struct User {
    pub id: i32,
    pub name: String,
    pub email: String,
    pub created_at: chrono::NaiveDateTime,
}

1.2. Подключение к MongoDB

Создадим файл src/mongodb.rs:

use mongodb::{Client, Collection, Database};
use mongodb::bson::{doc, Document};
use anyhow::Result;

pub async fn connect_to_mongodb() -> Result<(Database, Collection<Document>)> {
    let client = Client::with_uri_str("mongodb://localhost:27017")?;
    let database = client.database("mydb");
    let collection = database.collection::<Document>("users");
    println!("✅ Подключились к MongoDB");
    Ok((database, collection))
}

2. CRUD-операции в PostgreSQL

Теперь реализуем базовые операции: создание, чтение, обновление и удаление записей.

2.1. Вставка данных (CREATE)

pub async fn create_user(pool: &PgPool, name: &str, email: &str) -> Result<User> {
    let user = sqlx::query_as::<_, User>(
        "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id, name, email, created_at"
    )
    .bind(name)
    .bind(email)
    .fetch_one(pool)
    .await?;
    
    println!("Создан пользователь: {:?}", user);
    Ok(user)
}

2.2. Чтение данных (READ)

pub async fn get_user_by_id(pool: &PgPool, user_id: i32) -> Result<Option<User>> {
    let user = sqlx::query_as::<_, User>(
        "SELECT id, name, email, created_at FROM users WHERE id = $1"
    )
    .bind(user_id)
    .fetch_optional(pool)
    .await?;
    
    match user {
        Some(u) => println!("Найден пользователь: {:?}", u),
        None => println!("Пользователь с id={} не найден", user_id),
    }
    Ok(user)
}

2.3. Обновление и удаление (UPDATE/DELETE)

pub async fn update_user_email(pool: &PgPool, user_id: i32, new_email: &str) -> Result<u64> {
    let rows_affected = sqlx::query(
        "UPDATE users SET email = $1 WHERE id = $2"
    )
    .bind(new_email)
    .bind(user_id)
    .execute(pool)
    .await?
    .rows_affected();
    
    println!("Обновлено строк: {}", rows_affected);
    Ok(rows_affected)
}

pub async fn delete_user(pool: &PgPool, user_id: i32) -> Result<u64> {
    let rows_affected = sqlx::query(
        "DELETE FROM users WHERE id = $1"
    )
    .bind(user_id)
    .execute(pool)
    .await?
    .rows_affected();
    
    println!("Удалено строк: {}", rows_affected);
    Ok(rows_affected)
}

3. Работа с MongoDB: CRUD и агрегация

MongoDB работает с документами в формате BSON. Используем крейт mongodb для выполнения операций.

3.1. Вставка и поиск документов

use mongodb::bson::{doc, Document};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct MongoUser {
    pub name: String,
    pub email: String,
    pub age: u32,
    pub tags: Vec<String>,
}

pub async fn insert_user(collection: &Collection<Document>, user: &MongoUser) -> Result<()> {
    let doc = mongodb::bson::to_document(user)?;
    let result = collection.insert_one(doc).await?;
    println!("Вставлен документ с _id: {:?}", result.inserted_id);
    Ok(())
}

pub async fn find_users_by_age(collection: &Collection<Document>, min_age: u32) -> Result<Vec<Document>> {
    let filter = doc! { "age": { "$gte": min_age } };
    let mut cursor = collection.find(filter).await?;
    let mut users = Vec::new();
    
    while cursor.advance().await? {
        let user = cursor.current().clone();
        users.push(user);
    }
    
    println!("Найдено {} пользователей", users.len());
    Ok(users)
}

3.2.

Блоги

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