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

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

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

Начать курс

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

Rust — это современный системный язык программирования, который завоевывает всё большую популярность благодаря своей производительности, безопасности памяти и нулевой стоимости абстракций. Многие разработчики, приходящие из мира Python, JavaScript или Go, задаются вопросом: как работать с базами данных в Rust? Ответ прост: Rust предоставляет мощные и безопасные драйверы для работы как с реляционными (PostgreSQL), так и с документо-ориентированными (MongoDB) базами данных.

В этой статье мы разберем основы работы с PostgreSQL и MongoDB в Rust. Вы узнаете, как настроить проект, подключиться к базам данных и выполнять основные CRUD-операции. Статья ориентирована на начинающих, поэтому мы будем использовать простые и понятные примеры.

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

Перед началом работы убедитесь, что у вас установлен Rust и Cargo. Создайте новый проект:

cargo new rust_db_example
cd rust_db_example

Откройте файл Cargo.toml и добавьте необходимые зависимости. Для работы с PostgreSQL мы будем использовать крейт tokio-postgres (асинхронный драйвер) и serde для сериализации данных. Для MongoDB — официальный драйвер mongodb.

[dependencies]
tokio = { version = "1", features = ["full"] }
tokio-postgres = "0.7"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
mongodb = "2.8"

Обратите внимание: мы используем tokio как асинхронный рантайм, так как оба драйвера работают асинхронно. Это стандартная практика для современных Rust-приложений.

2. Работа с PostgreSQL в Rust

PostgreSQL — мощная реляционная база данных. Для работы с ней в Rust мы будем использовать крейт tokio-postgres. Он предоставляет асинхронный интерфейс для выполнения SQL-запросов.

2.1 Подключение к базе данных

Создадим функцию для подключения к локальной базе данных PostgreSQL. Убедитесь, что у вас запущен PostgreSQL сервер и создана база данных (например, test_db):

use tokio_postgres::{NoTls, Error};

#[tokio::main]
async fn main() -> Result<(), Error> {
    // Строка подключения: postgresql://user:password@localhost/dbname
    let (client, connection) = tokio_postgres::connect(
        "host=localhost user=postgres dbname=test_db",
        NoTls,
    ).await?;

    // Запускаем соединение в фоновом режиме
    tokio::spawn(async move {
        if let Err(e) = connection.await {
            eprintln!("Connection error: {}", e);
        }
    });

    println!("Подключение к PostgreSQL установлено!");
    Ok(())
}

2.2 Создание таблицы и вставка данных

Теперь создадим таблицу users и вставим несколько записей:

// Создаём таблицу
client
    .execute(
        "CREATE TABLE IF NOT EXISTS users (
            id SERIAL PRIMARY KEY,
            name VARCHAR(100) NOT NULL,
            email VARCHAR(150) UNIQUE NOT NULL
        )",
        &[],
    )
    .await?;

// Вставляем данные
let name = "Alice";
let email = "alice@example.com";
client
    .execute(
        "INSERT INTO users (name, email) VALUES ($1, $2)",
        &[&name, &email],
    )
    .await?;

println!("Пользователь {} добавлен!", name);

2.3 Чтение и обновление данных

Выполним запрос SELECT и обновим запись:

// Чтение данных
let rows = client
    .query("SELECT id, name, email FROM users", &[])
    .await?;

for row in rows {
    let id: i32 = row.get(0);
    let name: &str = row.get(1);
    let email: &str = row.get(2);
    println!("ID: {}, Name: {}, Email: {}", id, name, email);
}

// Обновление записи
client
    .execute(
        "UPDATE users SET email = $1 WHERE name = $2",
        &[&"alice_new@example.com", &"Alice"],
    )
    .await?;
println!("Email обновлён!");

Важно: В tokio-postgres для передачи параметров используются ссылки (&), что гарантирует безопасность типов и предотвращает SQL-инъекции.

3. Работа с MongoDB в Rust

MongoDB — популярная NoSQL база данных, хранящая данные в формате BSON (бинарный JSON). Официальный драйвер mongodb для Rust полностью асинхронен и интегрируется с tokio.

3.1 Подключение к MongoDB

Для подключения к локальному экземпляру MongoDB используйте следующий код:

use mongodb::{Client, options::ClientOptions};
use mongodb::bson::{doc, Document};

#[tokio::main]
async fn main() -> mongodb::error::Result<()> {
    let mut client_options = ClientOptions::parse("mongodb://localhost:27017").await?;
    client_options.app_name = Some("Rust App".to_string());

    let client = Client::with_options(client_options)?;
    let db = client.database("test_db");
    let collection = db.collection::<Document>("users");

    println!("Подключение к MongoDB установлено!");
    Ok(())
}

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

В MongoDB данные хранятся в виде документов. Используем структуру Document из крейта bson:

// Вставка одного документа
let new_user = doc! {
    "name": "Bob",
    "email": "bob@example.com",
    "age": 30
};
let insert_result = collection.insert_one(new_user).await?;
println!("Документ вставлен с ID: {:?}", insert_result.inserted_id);

// Поиск всех документов
let mut cursor = collection.find(doc! {}).await?;
while let Some(doc) = cursor.try_next().await? {
    println!("Найден документ: {:?}", doc);
}

3.3 Использование структур Serde

Для удобства работы можно определить структуру Rust и использовать serde для автоматической сериализации/десериализации:

use serde::{Deserialize, Serialize};

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

// Используем коллекцию с типом User
let collection = db.collection::<User>("users");

let user =                

Блоги

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