Rustのクレートtierリスト

こんばんは。本稿では自分が個人的な観点で便利なクレート、そうではないクレートを分類していきたいと思います。なるべく主観的な観点を排除して、客観的な観点からpros/consを記述できるように心がけますが、そうなっていない場合でもご了承ください。

## Goals
* 一般に役に立つとされるクレートの(非)網羅的なリストを作ること
* もはや*時代遅れ*とみなされるクレートの(非)網羅的なリストを作ること

## Non goals
* FFIバインディングはキリがないので比較用に示す場合以外は基本的に取り上げない。これらのクレートを探したいときはcrates.ioで[`external-ffi-bindings`](https://crates.io/categories/external-ffi-bindings)カテゴリを探索すること。既存のクレートが存在しない場合は自分で作ることもできるが、やはりこの記事のスコープを超える。

## Tier A
特定の用途において絶対外せない。

### シリアライズ・デシリアライズ
* `serde` - Rustのシリアライズ・デシリアライズといえばこれといっていいほど**非常に**よく見かける。`derive` featureを有効にするとシリアライズ・デシリアライズの実装が`derive`で生成できるので使うコストが非常に低い。対応しているデータ形式と連携クレートの多さも魅力。
  * `serde_json` - JSONをシリアライズ・デシリアライズする。
  * `serde_yaml` - YAMLをシリアライズ・デシリアライズする。`enum`はYAMLのタグとして書き込まれる。複数ドキュメントには未対応？
  * `serde_with` - `FromStr`の実装から`Deserialize`を導出したり`Display`の実装から`Serialize`を実装したりできる。
* `strum` - 文字列と`enum`を相互変換する。実装を`derive`することもできる。いちいち`impl FromStr for MyEnum`で`match`して〜などとやる手間を節約できる。

### エラー
* `anyhow` - `std::error::Error`を実装した`struct`を作るのが面倒で、*とりあえず*[^17]`Result`で「失敗」を表したいときに便利。`anyhow!`一行でエラーを作ることができ、`bail!`や`ensure!`でearly-`return`することができる。
* `thiserror` - `anyhow`から移行する時に便利。`enum`に`#[derive(Error, Debug)]`して使う。全てのvariantに`#[error("message")]`をつけると`Display`の実装も生えてくる。

[^17]: anyhowは英語で「とりあえず」「とにかく」などの意味を持つ。

### ユーティリティ
* `once_cell` - 初めて参照されたときに遅延初期化を行う`Lazy`と、一度だけ値を設定できる`OnceCell`が便利。Rust 1.70.0以降では`once_cell::unsync::Once`が`std::cell::OnceCell`で、`once_cell::sync::Once`が`std::cell::OnceLock`で置き換えることが可能になった。
* `reqwest` - HTTPクライアント。`serde`対応。`0.11.15`以降でHTTP/3の対応オプションが試験的に追加された。オプションで各種圧縮やJSONにも対応。`async`なAPIがデフォルトだが、オプションで非`async`なAPIも使える。
* `h3` - HTTP 3に対するクライアントの実装、及びサーバーの実装。

### proc-macro
* `syn` - Rustの抽象構文木をパースして扱いやすいように加工してくれる。
* `quote` - 式をproc-macroの出力に埋め込むことができる。
* `proc-macro2` - コンパイラ組み込みの`proc-macro`クレートの`TokenStream`を扱いやすい形に変換してくれる。`Into`トレイトの実装があるので変換するのも楽。

### コマンドライン
* `clap` - みんな大好きなコマンドラインのスイッチを`derive`で簡単に作れる。`derive`を使わないで手で全部設定することも一応できるが、使ったほうが (*拍手*したくなるほど) 便利。
  * `clap_complete` - `clap`を使っているコマンドでシェル補完を生成する。
* `is-terminal` - 標準入力・標準出力・標準エラー出力がターミナルか、あるいはパイプされているかを調べる。Rust 1.70.0以降を使えるならほとんど同じインターフェースのものが標準ライブラリで安定化されているのでそっちを使うべき。

## Tier B
特定の用途において便利。

### データ構造
* `email_address` - メールアドレス。`serde`対応。
* `url` - URL。`serde`対応。
* `ordered-float` - `NotNaN`で`f32`や`f64`がNaNではないことを約束すると`Ord`と`Eq`を`derive`することができるようになるので便利[^5]。Rustが1.62.0以降なら`OrderedFloat`の代わりに[`total_cmp`](https://doc.rust-lang.org/std/primitive.f32.html)が使える。
* `typenum` - [`#![feature(generic_const_exprs)]`](https://github.com/rust-lang/rust/issues/76560)の代わりとして、型で整数値をエンコーディングし、演算を可能にしたもの。
* `petgraph` - 有向グラフ・無向グラフを提供する。A*、ベルマンフォード法、ダイクストラ法、ワーシャルフロイド法、閉路検知など各種アルゴリズムも完備。ネットワークやダイアグラムを扱うときに便利。
* 

[^5]: `f32`や`f64`が`Eq`を実装していないのは、`NaN == NaN`が`false`を返すためである。`NaN`である可能性を排除すれば、それ以外の値`a`において`a == a`が`true`になるので、`Eq`を`derive`できる。

### 正規表現
* `regex` - 正規表現。マッチにかかる時間が線形時間であることが保証されている。名前付きキャプチャや貪欲ではない繰り返しが使えるなど、思っているより高機能。
* `fancy-regex` - 後読みと先読み、そして後方参照が使える`regex`。他にも条件付き正規表現やPythonと互換のある名前付きキャプチャなどが追加されているものの、世話になる機会は少ないだろう。線形時間であることは保証されない。
* `lazy-regex` - `regex!`マクロに正規表現を与えるとコンパイル時に書式の検査を行う。遅延初期化を行うので内部で`once_cell`を使っている。
* `pcre` - [PCRE](https://www.pcre.org/)に対するFFIバインディング。ただし、よっぽどのこと[^1]がない限り使わなくても済む。
* `onig` - 鬼車に対するFFIバインディング。文法は[GitHub](https://github.com/kkos/oniguruma/blob/master/doc/RE.ja)を参照されたい。

### 非同期
* [`async-trait`](https://crates.io/crates/async-trait) - proc-macroを使って`trait`の中で`async fn`を記述できるようにする[^4]。
* [`async-recursion`](https://crates.io/crates/async-recursion) - `async fn`を自己再帰できるようにする。
* [`tokio`](https://crates.io/crates/tokio)[^3] - 言わずと知れた`async fn`のランタイムの一つ。イベントキューやTCP/UDPの非同期操作など、いろいろな非同期ユーティリティーが入っている。雑に`full`を指定するとすべての機能が有効化されてコンパイルがかなり遅くなるので、必要なfeatureだけを指定すると良い。`macros`、`rt`、`rt-multi-thread`を指定して`tokio::main`を`main`関数につける使い方もあり。

[^4]: コンパイルエラーでもこのクレートに誘導される。

### 時刻
* `chrono` - `std::time`と違い、タイムゾーンの概念がデフォルトで組み込まれていて、日付と時刻を文字列化できる。それだけで使う価値がある。`serde`と連携するfeature gateを有効にすれば、日付や時刻をシリアライズ・デシリアライズするのもスムーズにいく。
  * `chrono_tz` - IANAのタイムゾーンを列挙した`enum`を追加する。`chrono`と連携する。

### コレクション
* `ref-cast` - strong typedefパターンかつ[`#[repr(transparent)]`](https://doc.rust-lang.org/nomicon/other-reprs.html#reprtransparent)がついている`struct`の参照から、安全に内側の値をその型の参照として取り出す。
* `maplit` - `vec!`のようなコレクションを構築するためのマクロを追加する。対応しているコレクションは`std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}`。
* `itertools` - `Iterator`に対してかゆいところに手が届くような拡張メソッドを定義する。
* [`portable-atomic`](https://crates.io/crates/portable-atomic) - プリミティブ型のアトミック操作を拡充する。
  1. アトミック操作をフルにサポートしていないコンパイラターゲットでもCompare-And-SwapをサポートするCPUならアトミック操作を「ちゃんと」できるように
  1. `#![no_std]`環境対応
  1. コンパイラのバグに対応
  1. 標準ライブラリが提供していない128ビット整数や浮動小数点数に対するアトミック操作を提供

### ユーティリティ
* `futures-util` - `FutureExt`や`TryFutureExt`といった拡張トレイトで`Future`の結果を`map`することができる。`TryFutureExt`なしで`map`しようとすると一度`.await`して`map`した上で`async {}`でくるまなければいけないので助かることがある。他にも`Iterator`の`async`版といえる`Stream`や、非同期IOのためのトレイトがある。
* `rand` - 疑似乱数。トレイトを実装することで数値以外も生成できる。暗号論的疑似乱数を求めているなら`rand_chacha`を使うこと。また、Rust 1.51.0以降を使えるのであれば任意長の配列を生成するために`min_const_gen` featureを有効にすることが推奨される (無効のままでも動くが、配列の長さが32以下あるいは2のべき乗の場合に限られる)。

### 文字列
* `ascii` - ASCII範囲内の文字列しか含まれないときに便利だと思う。具体的なユースケースはわからない。
  * 0.x系の一部のバージョンには可変参照同士で、制約を緩める方向への`From`トレイトの実装が存在する[脆弱性](https://github.com/rustsec/advisory-db/blob/main/crates/ascii/RUSTSEC-2023-0015.md)があるので注意。1.x系はこの脆弱性の影響を受けない。
* `widestring` - UTF-16とUTF-32の文字列を提供。`#![no_std]`対応。Windows APIなどのFFI向けにヌル終端された文字列、UTF-16やUTF-32**ではないかもしれない**文字列も存在する。

### syntax
* `if_chain` - nightlyの[`let_chains`](https://github.com/rust-lang/rust/issues/53667)の代わりとして使える。1.64で`let_chains`がstabilizeされて要らなくなるかに見えたが、[desugarのバグ](https://github.com/rust-lang/rust/issues/100513)があることが発覚し、un-stabilizeされたので引き続き必要。
* `cfg-if` - `cfg!`より強力な条件付きコンパイルにおけるグルーピングを提供する。https://stackoverflow.com/a/43070636/14900042 などが詳しい。[docs.rs](https://docs.rs/cfg-if/latest/cfg_if/)の例にあるように、`#[cfg(not(all(...)))]`が消えるのも好ましい。

### derive tweak
* `derivative` - `std`の`derive`できるトレイトに対して、`derive`が生成する実装のboundや考慮するフィールドから除外するフィールドなどを設定できるようにする。`PartialEq`を`derive`するときに特定のフィールドだけ除外したいときなどに役立つ。
* `derive_more` - `From`、`Into`を始めとした、組み込みトレイトだが`std`では`derive`できないトレイトを`derive`できるようにする。

### アルゴリズム
* `miller_rabin` - ミラー–ラビン素数判定法を使って素数である可能性があるかどうかを高速に判定する。特に、`u64`に収まる整数の場合、確定的に素数かどうか判定できる「証人」の素数の集合が明確になっているため**めちゃめちゃ**速い (誇張抜きに)。
* `aho_corasick` - エイホ[^2]–コラシック法を使って複数の部分文字列が含まれているポジションを高速に抜き出す。

## Tier C
特定の用途においては便利な場面もあるが、取り回しが効かなかったり、脆弱性を抱えていたりする。
* `async_std` - `std`のAPIを非同期にすることを目標にしている。`async fn`のランタイムの一つでもある。
* `atty` - stdin/stdout/stderrがttyかどうか調べるクレート。ただし、メンテナンスされておらずアライメントが合っていないメモリ読み込みの問題が[指摘されている](https://rustsec.org/advisories/RUSTSEC-2021-0145.html)ため前述した`is-terminal`に乗り換えるべき。
* `owning_ref` - READMEで`map`が使われているが、実装がunsoundであることが[指摘されている](https://rustsec.org/advisories/RUSTSEC-2022-0040.html)のでやめたほうがいいかも。
* `tokio-fiber` ([GitHub](https://github.com/dpc/tokio-fiber)) - 恐らく[tokio::task](https://docs.rs/tokio/latest/tokio/task/)に該当する。HRTBを`'static`に`transmute`[している](https://github.com/dpc/tokio-fiber/blob/master/src/lib.rs#L43-L51)のが懸念される。
* `ouroboros` - 自己参照を行うスマートポインタを定義する。しかし、以下の[脆弱性](https://rustsec.org/advisories/RUSTSEC-2023-0042.html) (とされる振る舞い) によって`self-cell`への以降が推奨されている。
  * Miriのバージョンアップによって「関数に渡された参照は`struct`の中に入っていてもその実行が終了するまで無効にならない」という検査が追加され、これによって所有している値とそれへの参照を同時に渡すことが不可能となった。
    * これを避けるために値をバイトの配列として見た上で、それへの参照を各関数の内部で取ることによって解決しようとした。
      * しかし、その型がパディングを含んでいた場合最適化によって"パディング (と**仮定していたスペース**)"に何らかの値が書き込まれる可能性を否定できず、Miriによって受け入れられるパディングのビットパターンが不明なため「いつもMiriを満足させられる初期化を行える」というほしょうができない。
  * また、ジェネリクスに対して`std::mem::size_of`を呼ぶことが[出来ない](https://doc.rust-lang.org/stable/error_codes/E0401.html) ([playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=07e6b7d72c7441986c4edbae53a70e1d)) という副次的な問題も指摘されている。

## Tier D
最新のコンパイラやライブラリが使えるなら自ら進んでピックする意味はない。ここで言う「Rust 1.xx.yが使えるなら」とは「プロジェクトに使用するコンパイラの最低バージョンを1.xx.y以上に設定できるなら」という意味である。

* `structopt` - `clap`と同じコマンドラインフラグの定義をできるクレートだが、メンテナンスモードに入っている。`clap` 3.x以降にAPIがほとんどそのまま取り入れられたので、移行するべき。
* `generic-array` - サイズがコンパイル時に確定している配列`[T; N]`の`N`を全称化するための構造体が入っている。内部的には`typenum`を使っている。Rust 1.51.0以降を使えるのであればconst genericsがあるので要らない。
* `unsafe_unwrap` - Rust 1.58.0以降を使えるなら同等の`unwrap_unchecked`が`core::option::Option`に[ある](https://doc.rust-lang.org/stable/core/option/enum.Option.html#method.unwrap_unchecked)ので要らない。
* `unsafe-any` - (現在で言うところの) [`std::any::Any`](https://doc.rust-lang.org/std/any/trait.Any.html)に対する[nightly feature](https://github.com/rust-lang/rust/issues/90850)の[`downcast_unchecked`](https://doc.rust-lang.org/std/any/trait.Any.html#method.downcast_unchecked)を切り出したパッケージ。ただし、deprecatedとマークされている上に後述の`traitobject`に依存しているのでこのクレートを使わないほうが良さそう。`downcast_unchecked`を使うか、[`downcast`](https://doc.rust-lang.org/std/any/trait.Any.html#method.downcast-1)した上で[`unwrap_unchecked`](https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap_unchecked)するのが良さそう。
* `cstr_core` - Rust 1.64.0以降を使えるのであれば`core::ffi::CStr`及び`alloc::ffi::CString`があるのであまり要らない\[要検証\]
* `futures` - [`core::future::Future`](https://doc.rust-lang.org/std/future/trait.Future.html)が使えるRust 1.36.0以降、もっと言えば`async`/`await`が安定化されたRust 1.39.0以降を使えるなら要らない\[要検証\]

## Tier F
使う価値が欠如している、あるいは簡単に乗り換えられる先があり使うべきではないもの。

* `uri` - 中身が何もない。`url`クレートを使うよう指示されている。
* `better-macro` - proc-macroを使って望まないページを表示させる。それ以外の中身は`println!`マクロと同一なので依存する価値が**一切**ない。
* `traitobject` - unsoundである可能性が[指摘](https://rustsec.org/advisories/RUSTSEC-2020-0027)されているが[メンテナンスされていない](https://rustsec.org/advisories/RUSTSEC-2021-0144.html)のでフォーク版の`destructure_traitobject`を使うべき。しかし、クレート名から考えるに、現代においてはコンパイラによってサポートされているtrait objectを使うほうが良さそうという印象を持った。
* `fake-static` - 任意のライフタイムを`'static`ライフタイムに「キャスト」する。**これは[型システムのバグ](https://github.com/rust-lang/rust/issues/25860)を突いている**。変数の寿命に関心があるならわかると思うが、一般にライフタイムは`'static`**ではない** (例: ローカル変数のライフタイムはその関数のスコープを上限として持つので明らかに`'static`ではない)。このクレートがやることは実質`core::mem::transmute`と同じだが、キャスト関数は`unsafe`がつけられていない。いずれにせよ**使うべきではない**。どうしてもやりたい場合は`core::mem::transmute`でやることを**強く推奨する**。このクレートは現在進行しているトレイト解決のシステムがリファクタリングされ、[`-Ztrait-solver=next`](https://github.com/rust-lang/rust/issues/107374)が既定で有効になったときに正しく動作しなくなる。
* `heap-vec` - 長さとキャパシティも含めてヒープに置く`Vec<T>`だが、未定義動作の可能性があり、怪しい。多分`tinyvec`か`smallvec`を使ったほうが良い。

[^1]: 以下の場合は`pcre`クレートを使わなくても目的が達成できる:
    * `\Q`...`\E`はエスケープシーケンスを部分的に無効化するシンタックスシュガー。
    * `\cX`は<kbd>Ctrl</kbd>と<kbd>X</kbd>キーを押したときに出力される文字のシンタックスシュガー。
    * Unicodeプロパティの`\p`とその否定版の`\P`は`regex`クレートでサポートされている。
    * 貪欲ではない繰り返し演算子`??`、`+?`、`*?`は`regex`クレートでサポートされている。
    * `\e`は`\x1B`のシンタックスシュガー。
    * `\U{hex}`はRust自体のエスケープシーケンスとして[使える](https://doc.rust-lang.org/reference/tokens.html#unicode-escapes)。
[^2]: 「*アホ*」ではない。
[^3]: アイドルグループからとったのでは[ない](https://www.reddit.com/r/rust/comments/4vzomj/comment/d62svqf/?utm_source=share&utm_medium=web2x&context=3)らしい。