Rust ORM のディープダイブ:Diesel と SQLx
Lukas Schneider
DevOps Engineer · Leapcell

はじめに
Web開発とデータ駆動型アプリケーションの世界では、オブジェクトリレーショナルマッパー(ORM)は、オブジェクト指向プログラミング言語とリレーショナルデータベースの間のギャップを埋める上で極めて重要な役割を果たします。ORMは、生のSQLクエリの複雑さを抽象化し、開発者が使い慣れた言語構造を使用してデータベースと対話できるようにします。安全性、パフォーマンス、並列処理に重点を置くRustでは、洗練されたORMソリューションが登場しています。これらのうち、DieselとSQLxは、データ整合性と開発者の生産性を確保するための独自の機能を提供しており、際立った選択肢となっています。この記事では、これら2つの強力なRust ORMを深く掘り下げ、そのコア哲学、実装の詳細、および実際の影響を調査し、それぞれの長所とユースケースの包括的な理解を提供します。
コアコンセプト
DieselとSQLxの詳細に入る前に、それらの操作を理解するために不可欠ないくつかの基本的な用語を確立しましょう。
- ORM(Object-Relational Mapper): データベーススキーマをオブジェクト指向パラダイムにマッピングするプログラミングツールであり、開発者はデータベースレコードをプログラミング言語内のオブジェクトとして操作できます。
- クエリビルダー: プログラムでSQLクエリを構築するのに役立つライブラリまたはコンポーネントで、多くの場合、SQLの構造に似たAPIを提供します。
- スキーママイグレーション: アプリケーションのデータモデルの変更に対応するために、時間の経過とともにデータベーススキーマを進化させるプロセスです。
- コンパイル時チェック: コンパイルプロセス中にコンパイラによって実行される検証で、コードの正確性と安全性を実行前に保証します。これはRustのコア原則です。
- マクロ: Rustにおけるコード生成メカニズムで、開発者が他のコードを生成するコードを書くことができます。これらは手続き型(
proc-macrosなど)または宣言型であり、メタプログラミングタスクによく使用されます。
Diesel: 型システムによるコンパイル時保証
Dieselは、Rustの堅牢な型システムを活用して、SQLクエリの正確性に関するコンパイル時保証を提供する、非常に包括的で型に厳格なORMです。アプリケーションが実行される前に、列名のタイプミスや型の一致の誤りなどの一般的なデータベースエラーを防ぐことを目指しています。
Diesel の仕組み
Dieselは、主にクエリビルダーとスキーマ管理を通じてコンパイル時チェックを実現します。データベーススキーマはRustで定義します。通常はdiesel print-schemaコマンドで生成されるschema.rsファイルを通じて定義します。このスキーマファイルには、データベーステーブルと列をミラーリングするRust型が含まれています。DieselのAPIを使用してクエリを構築すると、Rustコンパイラは操作が定義されたスキーマに一致していることを保証します。
例:スキーマの定義と Diesel によるクエリ
まず、データベースにpostsテーブルがあると仮定しましょう。
CREATE TABLE posts ( id SERIAL PRIMARY KEY, title VARCHAR NOT NULL, body TEXT NOT NULL, published BOOLEAN NOT NULL DEFAULT FALSE );
diesel print-schemaを使用すると、src/schema.rsに次のようなものが表示されます。
// @generated automatically by Diesel CLI. diesel::table! { posts (id) { id -> Int4, title -> Varchar, body -> Text, published -> Bool, } }
次に、このテーブルをククエリするDieselコードを記述しましょう。
use diesel::prelude::*; use diesel::PgConnection; // または使用しているデータベース #[derive(Queryable, Selectable)] #[diesel(table_name = crate::schema::posts)] pub struct Post { pub id: i32, pub title: String, pub body: String, pub published: bool, } pub fn establish_connection() -> PgConnection { let database_url = std::env::var("DATABASE_URL") .expect("DATABASE_URL must be set"); PgConnection::establish(&database_url) .unwrap_or_else(|_| panic!("Error connecting to {}\

