Работа с базами данных в Rust: PostgreSQL и MongoDB для начинающих

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

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

Начать курс

Работа с базами данных в Rust: PostgreSQL и MongoDB для начинающих

Rust — это современный системный язык программирования, который завоевывает популярность благодаря своей производительности и безопасности. Но как работать с базами данных в Rust? В этой статье мы разберем подключение и выполнение основных операций с двумя популярными СУБД: реляционной PostgreSQL и документо-ориентированной MongoDB. Материал рассчитан на начинающих разработчиков, знакомых с основами Rust.

Почему Rust и базы данных — хорошая комбинация?

Rust предлагает строгую систему типов и гарантии безопасности памяти, что особенно ценно при работе с данными. Ошибки доступа к памяти, типичные для C/C++, исключены на этапе компиляции. Кроме того, экосистема Rust включает зрелые библиотеки для взаимодействия с базами данных: diesel для PostgreSQL и официальный драйвер для MongoDB.

В этой статье мы научимся:

  • Подключаться к PostgreSQL и MongoDB из Rust
  • Выполнять CRUD-операции (создание, чтение, обновление, удаление)
  • Работать с результатами запросов безопасно и эффективно

1. Работа с PostgreSQL через библиотеку Diesel

Diesel — это ORM и query builder для Rust, который обеспечивает безопасность типов на уровне компиляции. Для начала работы добавьте зависимости в Cargo.toml:

[dependencies]
diesel = { version = "2.1", features = ["postgres"] }
dotenvy = "0.15"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1.0", features = ["full"] }

Настройка подключения

Создайте файл .env с URL вашей базы данных:

DATABASE_URL=postgres://user:password@localhost/mydb

Теперь напишем код для подключения и создания таблицы:

use diesel::prelude::*;
use diesel::pg::PgConnection;
use dotenvy::dotenv;
use std::env;

pub fn establish_connection() -> PgConnection {
    dotenv().ok();
    let database_url = env::var("DATABASE_URL")
        .expect("DATABASE_URL must be set");
    PgConnection::establish(&database_url)
        .unwrap_or_else(|_| panic!("Error connecting to {}", database_url))
}

Определение схемы и модели

Создадим таблицу users и соответствующую модель Rust:

// schema.rs
table! {
    users (id) {
        id -> Int4,
        name -> Varchar,
        email -> Varchar,
        created_at -> Timestamp,
    }
}

// models.rs
#[derive(Queryable, Selectable)]
#[diesel(table_name = crate::schema::users)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct User {
    pub id: i32,
    pub name: String,
    pub email: String,
    pub created_at: chrono::NaiveDateTime,
}

#[derive(Insertable)]
#[diesel(table_name = crate::schema::users)]
pub struct NewUser {
    pub name: String,
    pub email: String,
}

CRUD-операции

Пример создания и чтения пользователей:

use crate::models::{User, NewUser};
use crate::schema::users;

pub fn create_user(conn: &mut PgConnection, name: &str, email: &str) -> User {
    let new_user = NewUser {
        name: name.to_string(),
        email: email.to_string(),
    };

    diesel::insert_into(users::table)
        .values(&new_user)
        .returning(User::as_returning())
        .get_result(conn)
        .expect("Error saving new user")
}

pub fn get_all_users(conn: &mut PgConnection) -> Vec {
    users::table
        .load::(conn)
        .expect("Error loading users")
}

Как видите, Diesel проверяет типы и имена столбцов на этапе компиляции. Ошибка в имени поля приведет к ошибке компиляции, а не к runtime-сбою.

2. Работа с MongoDB через официальный драйвер

MongoDB — это NoSQL база данных, хранящая данные в формате JSON-подобных документов. Официальный драйвер для Rust является асинхронным и основан на tokio. Добавьте зависимости:

[dependencies]
mongodb = "2.8"
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

Подключение и структура данных

Определим модель пользователя и функцию подключения:

use mongodb::{Client, Database};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct User {
    pub name: String,
    pub email: String,
    pub age: u32,
}

pub async fn get_database() -> Database {
    let client = Client::with_uri_str("mongodb://localhost:27017")
        .await
        .expect("Failed to connect to MongoDB");
    client.database("mydb")
}

CRUD-операции с MongoDB

Пример вставки и поиска документов:

use mongodb::bson::doc;
use mongodb::options::FindOptions;

pub async fn insert_user(db: &Database, user: &User) {
    let collection = db.collection::("users");
    collection.insert_one(user)
        .await
        .expect("Failed to insert user");
    println!("User inserted: {:?}", user);
}

pub async fn find_users_by_age(db: &Database, min_age: u32) -> Vec {
    let collection = db.collection::("users");
    let filter = doc! { "age": { "$gte": min_age as i32 } };
    let options = FindOptions::builder()
        .sort(doc! { "name": 1 })
        .build();

    let mut cursor = collection.find(filter)
        .with_options(options)
        .await
        .expect("Failed to execute find query");

    let mut results = Vec::new();
    while let Some(user) = cursor.try_next().await.expect("Cursor error") {
        results.push(user);
    }
    results
}

MongoDB не требует предопределенной схемы, что дает гибкость, но требует внимательности при работе с документами разной структуры.

3. Сравнение и выбор подхода

Какую базу данных выбрать для вашего проекта на Rust? Рассмотрим ключевые различия:

  • PostgreSQL + Diesel — идеально для структурированных данных с четкими связями. Обеспечивает максимальную безопасность типов и целостность данных через миграции.
  • MongoDB — подходит для гибких схем, быстрого прототипирования и работы с большими объемами неструктурированных данных. Асинхронный драйвер легко интегрируется с tokio.

Блоги

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