Trabalho com bancos de dados em Rust: PostgreSQL e MongoDB na prática

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: Por que Rust e bancos de dados são uma combinação poderosa

Rust é uma linguagem de programação de sistema que combina o desempenho de C/C++ com a segurança de memória. No entanto, por muito tempo, o ecossistema de trabalho com bancos de dados em Rust foi considerado "imaturo". Hoje, a situação mudou drasticamente: as bibliotecas sqlx (para PostgreSQL) e o driver oficial mongodb fornecem APIs maduras, assíncronas e seguras.

Neste artigo, vamos explorar na prática como conectar-se ao PostgreSQL e ao MongoDB a partir do Rust, executar operações CRUD e tratar erros. Todos os exemplos usarão o runtime assíncrono tokio.



1. Configuração do projeto e instalação de dependências

Crie um novo projeto Cargo:

cargo new rust_db_examplecd rust_db_example

Adicione as seguintes dependências ao Cargo.toml:

[dependencies]tokio = { version = "1", features = ["full"] }sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres"] }mongodb = "2.8"serde = { version = "1", features = ["derive"] }anyhow = "1"
  • sqlx — driver assíncrono para PostgreSQL com verificação de consultas em tempo de compilação.
  • mongodb — driver oficial da MongoDB Inc.
  • serde — para serialização/desserialização de estruturas.
  • anyhow — para tratamento conveniente de erros.


2. Trabalho com PostgreSQL via sqlx

2.1 Conexão ao banco de dados

Para conectar, use uma string de conexão no formato postgres://user:password@localhost/dbname. Exemplo:

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

#[tokio::main]async fn main() -> Result<()> { let pool = PgPool::connect("postgres://postgres:password@localhost/testdb").await?; println!("Conexão ao PostgreSQL estabelecida!"); Ok(())}


2.2 Criação de tabela e inserção de dados

Vamos definir a estrutura User e executar a migração:

use serde::{Deserialize, Serialize};use sqlx::FromRow;

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

async fn create_table(pool: &PgPool) -> Result<()> { sqlx::query( "CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL UNIQUE )" ) .execute(pool) .await?; println!("Tabela users criada"); Ok(())}

async fn insert_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" ) .bind(name) .bind(email) .fetch_one(pool) .await?; Ok(user)}

Observe o uso de $1, $2 — são consultas parametrizadas que protegem contra injeção de SQL.



2.3 Leitura e atualização de dados

async fn get_user_by_email(pool: &PgPool, email: &str) -> Result<Option<User>> {    let user = sqlx::query_as::<_, User>("SELECT * FROM users WHERE email = $1")        .bind(email)        .fetch_optional(pool)        .await?;    Ok(user)}

async fn update_user_name(pool: &PgPool, id: i32, new_name: &str) -> Result<()> { sqlx::query("UPDATE users SET name = $1 WHERE id = $2") .bind(new_name) .bind(id) .execute(pool) .await?; println!("Usuário {} atualizado", id); Ok(())}

O uso de fetch_optional permite tratar o caso em que o registro não é encontrado, sem causar pânico.



3. Trabalho com MongoDB via driver oficial

3.1 Conexão e seleção do banco de dados

Blogs

Book Recommendations