Introduction: Why Rust and Databases are a Perfect Pair
Rust is a modern systems programming language that is gaining increasing popularity due to its performance, memory safety, and zero-cost abstractions. Many developers coming from the world of Python, JavaScript, or Go ask the question: how to work with databases in Rust? The answer is simple: Rust provides powerful and safe drivers for working with both relational (PostgreSQL) and document-oriented (MongoDB) databases.
In this article, we will cover the basics of working with PostgreSQL and MongoDB in Rust. You will learn how to set up a project, connect to databases, and perform basic CRUD operations. The article is aimed at beginners, so we will use simple and understandable examples.
1. Project Setup and Dependency Installation
Before starting, make sure you have Rust and Cargo installed. Create a new project:
cargo new rust_db_examplecd rust_db_exampleOpen the Cargo.toml file and add the necessary dependencies. For working with PostgreSQL, we will use the tokio-postgres crate (async driver) and serde for data serialization. For MongoDB, we will use the official mongodb driver.
[dependencies]tokio = { version = "1", features = ["full"] }tokio-postgres = "0.7"serde = { version = "1", features = ["derive"] }serde_json = "1"mongodb = "2.8"Note: we use tokio as the async runtime, since both drivers work asynchronously. This is standard practice for modern Rust applications.
2. Working with PostgreSQL in Rust
PostgreSQL is a powerful relational database. To work with it in Rust, we will use the tokio-postgres crate. It provides an asynchronous interface for executing SQL queries.
2.1 Connecting to the Database
Let's create a function to connect to a local PostgreSQL database. Make sure you have a PostgreSQL server running and a database created (e.g., test_db):
use tokio_postgres::{NoTls, Error};
#[tokio::main]async fn main() -> Result<(), Error> { // Connection string: postgresql://user:password@localhost/dbname let (client, connection) = tokio_postgres::connect( "host=localhost user=postgres dbname=test_db", NoTls, ).await?;
// Spawn the connection in the background tokio::spawn(async move { if let Err(e) = connection.await { eprintln!("Connection error: {}", e); } });
println!("Connection to PostgreSQL established!"); Ok(())}2.2 Creating a Table and Inserting Data
Now let's create a users table and insert some records:
// Create tableclient .execute( "CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(150) UNIQUE NOT NULL )", &[], ) .await?;
// Insert datalet name = "Alice";let email = "alice@example.com";client .execute( "INSERT INTO users (name, email) VALUES ($1, $2)", &[&name, &email], ) .await?;
println!("User {} added!", name);2.3 Reading and Updating Data
Let's execute a SELECT query and update a record:
// Read datalet 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);}
// Update a recordclient .execute( "UPDATE users SET email = $1 WHERE name = $2", &[&"alice_new