ORM for Rust: Complete Comparison and Usage Examples
The Rust ecosystem offers several powerful solutions for working with databases. In this article, we will compare three main approaches in detail: Diesel (synchronous, compile-time ORM), SeaORM (asynchronous, flexible), and SQLx (minimalistic, with compile-time query checking). You will learn which tool to choose for specific tasks and see working code examples.
1. Diesel — Proven Synchronous ORM
Diesel is the most mature ORM for Rust. Its main feature is type safety at compile time. All SQL queries are checked during compilation, eliminating an entire class of errors. It works only synchronously, but with tokio you can organize a connection pool.
Example of using Diesel:
// models.rs#[derive(Queryable, Insertable)]#[table_name = "users"]pub struct User { pub id: i32, pub name: String, pub email: String,}
// main.rsuse diesel::prelude::*;use diesel::dsl::insert_into;
fn main() { let conn = PgConnection::establish("postgres://user:pass@localhost/db") .expect("Database connection failed");
let new_user = User { id: 0, name: "Alice".into(), email: "alice@example.com".into(), };
let inserted = insert_into(users::table) .values(&new_user) .get_result::<User>(&conn) .expect("Insert failed");
println!("Created user: {}", inserted.name);}Pros: maximum performance, strict typing, built-in migrations.
Cons: synchronous, complex setup, does not support async out of the box.
2. SeaORM — Asynchronous and Flexible
SeaORM is a modern asynchronous ORM built on sqlx. It supports tokio and async-std. It offers a rich API for CRUD, relationships (one-to-one, one-to-many), and migrations. Ideal for web applications on Actix or Axum.
Example with SeaORM:
use sea_orm::*;use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]#[sea_orm(table_name = "users")]pub struct Model { #[sea_orm(primary_key)] pub id: i32, pub name: String, pub email: String,}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}
// Usageasync fn create_user(db: &DatabaseConnection) -> Result<(), DbErr> { let user = ActiveModel { name: Set("Bob".to_owned()), email: Set("bob@example.com".to_owned()), ..Default::default() }; let result = user.insert(db).await?; println!("User created with id: {}", result.id); Ok(())}Pros: full async support, supports PostgreSQL, MySQL, SQLite, simple migrations.
Cons: slower than Diesel in synthetic benchmarks, more dependencies.
3. SQLx — Lightweight and Safe
SQLx is not an ORM in the classical sense, but an asynchronous SQL driver with compile-time query checking. You write pure SQL, and SQLx checks its syntax and argument types. Supports PostgreSQL, MySQL, SQLite. An excellent choice if you prefer full control over queries.
SQLx example:
use sqlx::postgres::PgPool;
#[derive(sqlx::FromRow)]struct User { id: i32, name: String, email: String,}
#[tokio::main]async fn main() -> Result<(), sqlx::Error> { let pool = PgPool::connect("postgres://user:pass@localhost/db").await?;
let user = sqlx::query_as::<_, User>( "SELECT id, name, email FROM users WHERE email = $1" Also in library
Compose Multiplatform for Kotlin: Complete Guide with Examples
Internationalization (i18n) in C++: A Complete Guide
Working with CSV in Go (Golang): A Complete Guide to the encoding/csv Package
GraphQL for Kotlin: Powerful Libraries for Building APIs