日本の山岳一覧・百名山 API を開発しましたCLICK !

Rust | ulid クレートを使って ULID を生成する

Rust ULID
  • URLをコピーしました!

ulid クレートを使って ULID を生成してみたいと思います。

ULID を生成する

  1. ulid クレートを使います
  2. ulid::new()で生成
  3. to_string()で変換
目次

ULID を生成する

ULID とは

ULID は Universally Unique Lexicographically Sortable Identifier のことで、

簡単に説明すると ソートできる UUID です。

UUID の上位互換である ULID を使う機会が多くなってきた気がします。

あくまで自分自身が観測できる範囲の話ではありますが…笑

ulid クレート

crates.io で ulid と検索すると、一番にヒットするクレート ulid を使います。

[dependencies]
ulid = "1.0.0"

ulid::new() で生成できる

ざっと、簡単にまとめると以下のようになります。

  • newで構造体 Ulid を生成
  • to_stringで String に変換
  • from_stringで構造体 Ulid に戻すことも可能
use ulid::Ulid;

fn main() {
    // Ulid を生成
    let ulid = Ulid::new();
    println!("{:?}", ulid);

    // String に変換
    let id = ulid.to_string();
    println!("{:?}", id);

    // Ulid に戻す
    let result = Ulid::from_string(&id);
    println!("{:?}", result);

    assert_eq!(Ok(ulid), result);
}

Id オブジェクトとして実装してみる

Axum と SQLx を使って Todo アプリ を作成しているので、そこからコードを拝借します。

各モデルが共通で id としてId<T>を持つように設計しています。

Todoモデルや新規作成時に使用するNewTodoは以下のようになっています。

todo-kernel/src/model/todo.rs

pub struct Todo {
    pub id: Id<Todo>,
    pub title: String,
    pub description: String,
    pub status: TodoStatus,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

pub struct NewTodo {
    pub id: Id<Todo>,
    pub title: String,
    pub description: String,
}

impl NewTodo {
    pub fn new(id: Id<Todo>, title: String, description: String) -> Self {
        Self {
            id,
            title,
            description,
        }
    }
}

Id<T>の実装は以下です。

todo-kernel/src/model/todo.rs

use anyhow::anyhow;
use std::marker::PhantomData;
use ulid::Ulid;

pub mod todo;

#[derive(Debug)]
pub struct Id<T> {
    pub value: Ulid,
    _marker: PhantomData<T>,
}

impl<T> Id<T> {
    pub fn new(value: Ulid) -> Self {
        Self {
            value,
            _marker: PhantomData,
        }
    }

    pub fn gen() -> Id<T> {
        Id::new(Ulid::new())
    }
}

impl<T> TryFrom<String> for Id<T> {
    type Error = anyhow::Error;

    fn try_from(value: String) -> Result<Self, Self::Error> {
        Ulid::from_string(&value)
            .map(|id| Self::new(id))
            .map_err(|err| anyhow!("{:?}", err))
    }
}

ユースケース層に定義した Todo 作成時に使用するCreateTodoからNewTodoへ値の詰め替えをおこなう際、Id::gen()を呼び出して ULID を生成しています。

todo-app/src/model/todo.rs

pub struct CreateTodo {
    pub title: String,
    pub description: String,
}

impl CreateTodo {
    pub fn new(title: String, description: String) -> Self {
        Self { title, description }
    }
}

impl TryFrom<CreateTodo> for NewTodo {
    type Error = anyhow::Error;

    fn try_from(ct: CreateTodo) -> Result<Self, Self::Error> {
        Ok(NewTodo::new(Id::gen(), ct.title, ct.description))
    }
}

また、パスパラメータで渡ってきた String 型の Todo ID をId<Todo>に変換する際など、ULIDの形式か検証をおこなう必要があるので、TryFromを実装しています。

todo-app/src/usecase/todo.rs

pub async fn get_todo(&self, id: String) -> anyhow::Result<Option<TodoView>> {
    let resp = self
        .repositories
        .todo_repository()
        .get(&id.try_into()?)
        .await?;

    match resp {
        Some(t) => Ok(Some(t.into())),
        None => Ok(None),
    }
}

rusty_ulid というクレートもある

ulid クレート以外にも、rusty_ulid というクレートもありました。

newなのか、generateなのかの差ぐらいでしょうか。

use std::str::FromStr;
use rusty_ulid::Ulid;

fn main() {
    // Ulid を生成
    let ulid = Ulid::generate();

    // String に変換
    let ulid_string = ulid.to_string();

    // Ulid に戻す
    let result = Ulid::from_str(&ulid_string);

    assert_eq!(Ok(ulid), result);
}

2022/9/3 時点のダウンロード数や Github Star の数をまとめました。

All-Time のダウンロード数には、かなり差がありました。

ただ、Recent ではその差が埋まりつつあるので、甲乙つけがたい気がします…

バージョンダウンロード数Github Star
ulid1.0.0All-Time: 164,694
Recent: 60,813
143
rusty_ulid1.0.0All-Time: 549,778
Recent: 76,675
26

まとめ

Rust で ULID を扱う際に便利なクレート ulid のご紹介でした!

Rust ULID

この記事が気に入ったら
フォローしてね!

  • URLをコピーしました!

コメント

コメントする

目次