Introduction
RustでDBなどから取得したデータをモデル化して使う場合、
下記のようにidをDBの形式に合わせて持たせることが多いです。
struct Foo {
id : u32,
bar : String
}
これはこれでよいのですが、例えばFoo構造体のidとBar構造体のidは
同じu32型であっても別のものとして扱いたいところです。
そんなときに使えるのが、今回紹介するTypedIdです。
TypedIdは汎用的なラッパーであり、idに対して単一の型を導入できます。
TypedIdのインスタンスには、対象の構造体にそのid型を関連付ける
ためのgeneric parameterがあります。
ではTypedIdを使ってみましょう。
Environments
- MacBook Pro (13-inch, M1, 2020)
- OS : MacOS 13.5.2
- Rust : 1.75.0
Try
Setup
Cargoでプロジェクトを作成したあと、
TypedId crateを追加します。
% cargo new typedid-example
% cd /path/your/typedid-example
% cargo add typed_id paste
pasteはマクロ内で新しい識別子を生成するcrateで、
後述するid_type!を使うときに必要です。
実装してみる
src/main.rsでTypedIdをつかってみましょう。
下記のように、User型、Customer型を定義し、
そのidとしてUserId型、CustomerId型を使っています。
id型はTypedIdをつかって実際の型(u32)と、どの構造体の型なのかを
指定しています。
use typed_id::TypedId;
#[derive(Debug)]
pub struct User {
id: UserId,
name: String
}
#[derive(Debug)]
pub struct Customer {
id: CustomerId,
name: String
}
pub type UserId = TypedId<u32, User>;
pub type CustomerId = TypedId<u32, Customer>;
構造体のインスタンスを作成してassertで比較してみます。
UserとCustomerのidはおなじ値でも、
比較すると違うものとして扱われます。
fn main() {
let id = 10;
let user = User {
id: id.into(),
name: String::from("taro")
};
let customer = Customer {
id: id.into(),
name: String::from("customer")
};
assert_eq!(id, *user.id);
assert_eq!(id, *customer.id);
assert_eq!(*user.id,*customer.id);
}
id_type!マクロを使えば、各Id型をtypeで定義する必要はありません。
use typed_id::id_type;
pub struct Order { id: OrderId }
//pub type OrderId = TypedId<u32, Order> と同じ意味
id_type!(u32, Order);