Trabajo con bases de datos en Rust: PostgreSQL y MongoDB en la práctica

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: Por qué Rust y las bases de datos son una combinación poderosa

Rust es un lenguaje de programación de sistemas que combina el rendimiento de C/C++ con la seguridad de la memoria. Sin embargo, durante mucho tiempo el ecosistema para trabajar con bases de datos en Rust se consideró "inmaduro". Hoy la situación ha cambiado drásticamente: las bibliotecas sqlx (para PostgreSQL) y el controlador oficial mongodb proporcionan APIs maduras, asíncronas y seguras.

En este artículo analizaremos en la práctica cómo conectarse a PostgreSQL y MongoDB desde Rust, realizar operaciones CRUD y manejar errores. Todos los ejemplos usarán el runtime asíncrono tokio.



1. Configuración del proyecto e instalación de dependencias

Cree un nuevo proyecto de Cargo:

cargo new rust_db_examplecd rust_db_example

Agregue las siguientes dependencias en 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 — controlador asíncrono para PostgreSQL con verificación de consultas en tiempo de compilación.
  • mongodb — controlador oficial de MongoDB Inc.
  • serde — para serialización/deserialización de estructuras.
  • anyhow — para el manejo conveniente de errores.


2. Trabajo con PostgreSQL mediante sqlx

2.1 Conexión a la base de datos

Para la conexión, use una cadena de conexión del tipo postgres://user:password@localhost/dbname. Ejemplo:

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

#[tokio::main]async fn main() -> Result<()> { let pool = PgPool::connect("postgres://postgres:password@localhost/testdb").await?; println!("¡Conexión a PostgreSQL establecida!"); Ok(())}


2.2 Creación de tabla e inserción de datos

Definamos la estructura User y realicemos la migración:

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!("Tabla users creada"); 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 $1, $2 — son consultas parametrizadas que protegen contra inyecciones SQL.



2.3 Lectura y actualización de datos

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!("Usuario {} actualizado", id); Ok(())}

El uso de fetch_optional permite manejar el caso en que no se encuentra el registro, sin entrar en pánico.



3. Trabajo con MongoDB mediante el controlador oficial

3.1 Conexión y selección de base de datos

Blogs

Book Recommendations