在 Rust 中处理数据库:PostgreSQL 和 MongoDB 实战

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么 Rust 和数据库是强大的组合

Rust 是一种系统编程语言,它结合了 C/C++ 的性能和内存安全性。然而,长期以来,Rust 的数据库处理生态被认为是“不成熟的”。如今,情况已发生根本性改变:sqlx(用于 PostgreSQL)和官方 mongodb 驱动库提供了成熟、异步且安全的 API。

在本文中,我们将通过实践学习如何从 Rust 连接到 PostgreSQL 和 MongoDB,执行 CRUD 操作并处理错误。所有示例都将使用异步运行时 tokio。



1. 项目设置和依赖安装

创建一个新的 Cargo 项目:

cargo new rust_db_examplecd rust_db_example

在 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 — 用于 PostgreSQL 的异步驱动,支持编译时查询检查。
  • mongodb — MongoDB Inc. 的官方驱动。
  • serde — 用于结构的序列化/反序列化。
  • anyhow — 用于便捷的错误处理。


2. 通过 sqlx 处理 PostgreSQL

2.1 连接数据库

使用格式为 postgres://user:password@localhost/dbname 的连接字符串进行连接。示例:

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

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


2.2 创建表并插入数据

定义 User 结构并执行迁移:

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!("已创建 users 表"); 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)}

注意 $1, $2 — 这是参数化查询,可防止 SQL 注入。



2.3 读取和更新数据

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!("用户 {} 已更新", id); Ok(())}

使用 fetch_optional 可以在未找到记录时优雅地处理,而不会引发 panic。



3. 通过官方驱动处理 MongoDB

3.1 连接并选择数据库

博客

书籍推荐