Введение: Почему 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 =