ORM for Rust: Detailed Comparison and Usage Examples
The Rust ecosystem offers several powerful solutions for working with databases. In this article, we will look at three of the most popular ORMs (Object-Relational Mapping) for Rust: Diesel, SeaORM, and SQLx. Each has its own strengths and is suitable for different scenarios. We will compare them by key characteristics and provide practical code examples.
1. Diesel — Classic ORM with Maximum Performance
Diesel is the most mature ORM in the Rust ecosystem. It works synchronously (with async support via diesel-async) and is known for its high performance and type safety.
Key Features:
- Full type safety: SQL errors are caught at compile time
- Support for PostgreSQL, MySQL, SQLite
- Schema generation via the
diesel_cliCLI tool - Optimized for complex queries and transactions
Code Example with Diesel:
// Model definition#[derive(Queryable, Insertable)]#[diesel(table_name = crate::schema::users)]struct User { id: i32, name: String, email: String,}
// Inserting a new userfn create_user(conn: &mut PgConnection, name: &str, email: &str) -> User { let new_user = NewUser { name, email }; diesel::insert_into(users::table) .values(&new_user) .get_result(conn) .expect("Error saving new user")}
// Querying all usersfn get_all_users(conn: &mut PgConnection) -> Vec<User> { users::table .load::<User>(conn) .expect("Error loading users")}2. SeaORM — Async ORM with Active Model
SeaORM is a modern asynchronous ORM, inspired by Entity Framework and Active Record. It provides a convenient API for building queries and supports migrations out of the box.
Key Features:
- Async by default (based on Tokio)
- Active model: methods can be called on entities
- Support for relationships (has_many, belongs_to, many_to_many)
- Built-in migrations and CLI utility
Code Example with SeaORM:
use sea_orm::{entity::prelude::*, Database, EntityTrait};
// Entity definition#[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 {}
// Usage in codeasync fn run() { let db = Database::connect("postgres://user:pass@localhost/db").await.unwrap(); // Insert let user = ActiveModel { name: Set("Alice".to_owned()), email: Set("alice@example.com".to_owned()), ..Default::default() }; let result = Entity::insert(user).exec(&db).await.unwrap(); // Select all let users: Vec<Model> = Entity::find().all(&db).await.unwrap();}3. SQLx — Not an ORM, but an Async SQL Driver with Compile-Time Checking
SQLx occupies a middle ground: it is not a full-fledged ORM, but it provides powerful tools for working with SQL with compile-time query checking. It is ideal for those who prefer to write pure SQL but want to avoid errors.
Key Features:
- Async (Tokio, async-std)
- Compile-time SQL query checking via the
sqlx::query!macro - Support for PostgreSQL, MySQL, SQLite, MSSQL
- Lightweight, no ORM magic