ORM pour Rust : comparaison complète et exemples d'utilisation
L'écosystème Rust propose plusieurs solutions puissantes pour travailler avec les bases de données. Dans cet article, nous comparerons en détail trois approches principales : Diesel (ORM synchrone et compilé), SeaORM (asynchrone et flexible) et SQLx (minimaliste, avec vérification des requêtes à la compilation). Vous découvrirez quel outil choisir pour des tâches spécifiques et verrez des exemples de code fonctionnels.
1. Diesel — un ORM synchrone éprouvé
Diesel est l'ORM le plus mature pour Rust. Sa principale caractéristique est la sécurité des types à la compilation. Toutes les requêtes SQL sont vérifiées lors de la construction, ce qui élimine toute une classe d'erreurs. Il fonctionne uniquement de manière synchrone, mais avec tokio, il est possible d'organiser un pool de connexions.
Exemple d'utilisation de 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("Échec de la connexion à la base de données");
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("Échec de l'insertion");
println!("Utilisateur créé : {}", inserted.name);}Avantages : performances maximales, typage strict, migrations intégrées.
Inconvénients : synchrone, configuration complexe, ne prend pas en charge async nativement.
2. SeaORM — asynchrone et flexible
SeaORM est un ORM asynchrone moderne, construit sur sqlx. Il prend en charge tokio et async-std. Il offre une API riche pour les opérations CRUD, les relations (un-à-un, un-à-plusieurs) et les migrations. Idéal pour les applications web avec Actix ou Axum.
Exemple avec 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 {}
// Utilisationasync 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!("Utilisateur créé avec l'id : {}", result.id); Ok(())}Avantages : async complet, prise en charge de PostgreSQL, MySQL, SQLite, migrations simples.
Inconvénients : plus lent que Diesel dans les tests synthétiques, plus de dépendances.
3. SQLx — léger et sécurisé
SQLx n'est pas un ORM au sens classique, mais un pilote SQL asynchrone avec vérification des requêtes à la compilation. Vous écrivez du SQL pur, et SQLx vérifie sa syntaxe et les types des arguments. Il prend en charge PostgreSQL, MySQL, SQLite. Un excellent choix si vous préférez un contrôle total sur les requêtes.
Exemple avec SQLx :
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"