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

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

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

Начать курс

Введение в работу с базами данных на языке Rust

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

Мы рассмотрим основные библиотеки (крейты), такие как diesel для PostgreSQL и официальный драйвер mongodb, а также покажем примеры CRUD-операций на чистом Rust. Статья будет полезна как новичкам, так и опытным разработчикам, желающим расширить свой стек технологий.

1. Работа с PostgreSQL: крейт Diesel

Diesel — это безопасный, расширяемый ORM и конструктор запросов для Rust. Он обеспечивает проверку типов на этапе компиляции, что исключает множество ошибок времени выполнения, связанных с SQL-запросами. Для начала работы необходимо добавить зависимости в файл 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"] }

Также установите утилиту командной строки Diesel CLI:

cargo install diesel_cli --no-default-features --features postgres

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

DATABASE_URL=postgres://username:password@localhost/my_database

Выполните команду для настройки миграций:

diesel setup
diesel migration generate create_users

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

В файле миграции up.sql напишите SQL для создания таблицы:

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR NOT NULL,
    email VARCHAR NOT NULL UNIQUE,
    age INTEGER NOT NULL
);

Затем выполните diesel migration run. После этого сгенерируйте схему Rust (файл src/schema.rs) с помощью:

diesel print-schema > src/schema.rs

Теперь определим модель и структуру для вставки данных:

// src/models.rs
use diesel::prelude::*;
use serde::{Deserialize, Serialize};
use crate::schema::users;

#[derive(Queryable, Selectable, Serialize, Deserialize)]
#[diesel(table_name = users)]
pub struct User {
    pub id: i32,
    pub name: String,
    pub email: String,
    pub age: i32,
}

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

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

Рассмотрим основные операции. Подключение к БД и создание нового пользователя:

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

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

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

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

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

Чтение всех пользователей:

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

Обновление возраста пользователя по ID:

use diesel::associations::HasTable;

pub fn update_user_age(conn: &mut PgConnection, user_id: i32, new_age: i32) -> usize {
    diesel::update(users.find(user_id))
        .set(age.eq(new_age))
        .execute(conn)
        .expect("Error updating user")
}

Удаление пользователя:

pub fn delete_user(conn: &mut PgConnection, user_id: i32) -> usize {
    diesel::delete(users.find(user_id))
        .execute(conn)
        .expect("Error deleting user")
}

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

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

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

Добавьте зависимости в Cargo.toml:

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

Пример подключения к локальной MongoDB:

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

#[derive(Debug, Serialize, Deserialize)]
struct User {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    id: Option<bson::oid::ObjectId>,
    name: String,
    email: String,
    age: i32,
}

#[tokio::main]
async fn main() -> mongodb::error::Result<()> {
    let client = Client::with_uri_str("mongodb://localhost:27017").await?;
    let database = client.database("my_database");
    let collection: Collection<User> = database.collection("users");
    Ok(())
}

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

Создание (Insert):

async fn create_user(collection: &Collection<User>, user: User) -> mongodb::error::Result<()> {                

Блоги

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