こんばんは。本稿では自分が個人的な観点で便利なクレート、そうではないクレートを分類していきたいと思います。なるべく主観的な観点を排除して、客観的な観点からpros/consを記述できるように心がけますが、そうなっていない場合でもご了承ください。
Goals
- 一般に役に立つとされるクレートの(非)網羅的なリストを作ること
- もはや時代遅れとみなされるクレートの(非)網羅的なリストを作ること
Non goals
- FFIバインディングはキリがないので比較用に示す場合以外は基本的に取り上げない。これらのクレートを探したいときはcrates.ioで
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
を作るのが面倒で、とりあえず1Result
で「失敗」を表したいときに便利。anyhow!
一行でエラーを作ることができ、bail!
やensure!
でearly-return
することができる。 -
thiserror
-anyhow
から移行する時に便利。enum
に#[derive(Error, Debug)]
して使う。全てのvariantに#[error("message")]
をつけるとDisplay
の実装も生えてくる。
ユーティリティ
-
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
することができるようになるので便利2。Rustが1.62.0以降ならOrderedFloat
の代わりにtotal_cmp
が使える。 -
typenum
-#![feature(generic_const_exprs)]
の代わりとして、型で整数値をエンコーディングし、演算を可能にしたもの。 -
petgraph
- 有向グラフ・無向グラフを提供する。A*、ベルマンフォード法、ダイクストラ法、ワーシャルフロイド法、閉路検知など各種アルゴリズムも完備。ネットワークやダイアグラムを扱うときに便利。
正規表現
-
regex
- 正規表現。マッチにかかる時間が線形時間であることが保証されている。名前付きキャプチャや貪欲ではない繰り返しが使えるなど、思っているより高機能。 -
fancy-regex
- 後読みと先読み、そして後方参照が使えるregex
。他にも条件付き正規表現やPythonと互換のある名前付きキャプチャなどが追加されているものの、世話になる機会は少ないだろう。線形時間であることは保証されない。 -
lazy-regex
-regex!
マクロに正規表現を与えるとコンパイル時に書式の検査を行う。遅延初期化を行うので内部でonce_cell
を使っている。 -
pcre
- PCREに対するFFIバインディング。ただし、よっぽどのこと3がない限り使わなくても済む。 -
onig
- 鬼車に対するFFIバインディング。文法はGitHubを参照されたい。
非同期
-
async-trait
- proc-macroを使ってtrait
の中でasync fn
を記述できるようにする4。 -
async-recursion
-async fn
を自己再帰できるようにする。 -
tokio
5 - 言わずと知れたasync fn
のランタイムの一つ。イベントキューやTCP/UDPの非同期操作など、いろいろな非同期ユーティリティーが入っている。雑にfull
を指定するとすべての機能が有効化されてコンパイルがかなり遅くなるので、必要なfeatureだけを指定すると良い。macros
、rt
、rt-multi-thread
を指定してtokio::main
をmain
関数につける使い方もあり。
時刻
-
chrono
-std::time
と違い、タイムゾーンの概念がデフォルトで組み込まれていて、日付と時刻を文字列化できる。それだけで使う価値がある。serde
と連携するfeature gateを有効にすれば、日付や時刻をシリアライズ・デシリアライズするのもスムーズにいく。-
chrono_tz
- IANAのタイムゾーンを列挙したenum
を追加する。chrono
と連携する。
-
コレクション
-
ref-cast
- strong typedefパターンかつ#[repr(transparent)]
がついているstruct
の参照から、安全に内側の値をその型の参照として取り出す。 -
maplit
-vec!
のようなコレクションを構築するためのマクロを追加する。対応しているコレクションはstd::collections::{BTreeMap, BTreeSet, HashMap, HashSet}
。 -
itertools
-Iterator
に対してかゆいところに手が届くような拡張メソッドを定義する。 -
portable-atomic
- プリミティブ型のアトミック操作を拡充する。- アトミック操作をフルにサポートしていないコンパイラターゲットでもCompare-And-SwapをサポートするCPUならアトミック操作を「ちゃんと」できるように
-
#![no_std]
環境対応 - コンパイラのバグに対応
- 標準ライブラリが提供していない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
トレイトの実装が存在する脆弱性があるので注意。1.x系はこの脆弱性の影響を受けない。
- 0.x系の一部のバージョンには可変参照同士で、制約を緩める方向への
-
widestring
- UTF-16とUTF-32の文字列を提供。#![no_std]
対応。Windows APIなどのFFI向けにヌル終端された文字列、UTF-16やUTF-32ではないかもしれない文字列も存在する。
syntax
-
if_chain
- nightlyのlet_chains
の代わりとして使える。1.64でlet_chains
がstabilizeされて要らなくなるかに見えたが、desugarのバグがあることが発覚し、un-stabilizeされたので引き続き必要。 -
cfg-if
-cfg!
より強力な条件付きコンパイルにおけるグルーピングを提供する。https://stackoverflow.com/a/43070636/14900042 などが詳しい。docs.rsの例にあるように、#[cfg(not(all(...)))]
が消えるのも好ましい。
derive tweak
-
derivative
-std
のderive
できるトレイトに対して、derive
が生成する実装のboundや考慮するフィールドから除外するフィールドなどを設定できるようにする。PartialEq
をderive
するときに特定のフィールドだけ除外したいときなどに役立つ。 -
derive_more
-From
、Into
を始めとした、組み込みトレイトだがstd
ではderive
できないトレイトをderive
できるようにする。
アルゴリズム
-
miller_rabin
- ミラー–ラビン素数判定法を使って素数である可能性があるかどうかを高速に判定する。特に、u64
に収まる整数の場合、確定的に素数かどうか判定できる「証人」の素数の集合が明確になっているためめちゃめちゃ速い (誇張抜きに)。 -
aho_corasick
- エイホ6–コラシック法を使って複数の部分文字列が含まれているポジションを高速に抜き出す。
Tier C
特定の用途においては便利な場面もあるが、取り回しが効かなかったり、脆弱性を抱えていたりする。
-
async_std
-std
のAPIを非同期にすることを目標にしている。async fn
のランタイムの一つでもある。 -
atty
- stdin/stdout/stderrがttyかどうか調べるクレート。ただし、メンテナンスされておらずアライメントが合っていないメモリ読み込みの問題が指摘されているため前述したis-terminal
に乗り換えるべき。 -
owning_ref
- READMEでmap
が使われているが、実装がunsoundであることが指摘されているのでやめたほうがいいかも。 -
tokio-fiber
(GitHub) - 恐らくtokio::taskに該当する。HRTBを'static
にtransmute
しているのが懸念される。 -
ouroboros
- 自己参照を行うスマートポインタを定義する。しかし、以下の脆弱性 (とされる振る舞い) によってself-cell
への以降が推奨されている。- Miriのバージョンアップによって「関数に渡された参照は
struct
の中に入っていてもその実行が終了するまで無効にならない」という検査が追加され、これによって所有している値とそれへの参照を同時に渡すことが不可能となった。- これを避けるために値をバイトの配列として見た上で、それへの参照を各関数の内部で取ることによって解決しようとした。
- しかし、その型がパディングを含んでいた場合最適化によって"パディング (と仮定していたスペース)"に何らかの値が書き込まれる可能性を否定できず、Miriによって受け入れられるパディングのビットパターンが不明なため「いつもMiriを満足させられる初期化を行える」というほしょうができない。
- これを避けるために値をバイトの配列として見た上で、それへの参照を各関数の内部で取ることによって解決しようとした。
- また、ジェネリクスに対して
std::mem::size_of
を呼ぶことが出来ない (playground) という副次的な問題も指摘されている。
- Miriのバージョンアップによって「関数に渡された参照は
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
にあるので要らない。 -
unsafe-any
- (現在で言うところの)std::any::Any
に対するnightly featureのdowncast_unchecked
を切り出したパッケージ。ただし、deprecatedとマークされている上に後述のtraitobject
に依存しているのでこのクレートを使わないほうが良さそう。downcast_unchecked
を使うか、downcast
した上でunwrap_unchecked
するのが良さそう。 -
cstr_core
- Rust 1.64.0以降を使えるのであればcore::ffi::CStr
及びalloc::ffi::CString
があるのであまり要らない[要検証] -
futures
-core::future::Future
が使えるRust 1.36.0以降、もっと言えばasync
/await
が安定化されたRust 1.39.0以降を使えるなら要らない[要検証]
Tier F
使う価値が欠如している、あるいは簡単に乗り換えられる先があり使うべきではないもの。
-
uri
- 中身が何もない。url
クレートを使うよう指示されている。 -
better-macro
- proc-macroを使って望まないページを表示させる。それ以外の中身はprintln!
マクロと同一なので依存する価値が一切ない。 -
traitobject
- unsoundである可能性が指摘されているがメンテナンスされていないのでフォーク版のdestructure_traitobject
を使うべき。しかし、クレート名から考えるに、現代においてはコンパイラによってサポートされているtrait objectを使うほうが良さそうという印象を持った。 -
fake-static
- 任意のライフタイムを'static
ライフタイムに「キャスト」する。これは型システムのバグを突いている。変数の寿命に関心があるならわかると思うが、一般にライフタイムは'static
ではない (例: ローカル変数のライフタイムはその関数のスコープを上限として持つので明らかに'static
ではない)。このクレートがやることは実質core::mem::transmute
と同じだが、キャスト関数はunsafe
がつけられていない。いずれにせよ使うべきではない。どうしてもやりたい場合はcore::mem::transmute
でやることを強く推奨する。このクレートは現在進行しているトレイト解決のシステムがリファクタリングされ、-Ztrait-solver=next
が既定で有効になったときに正しく動作しなくなる。 -
heap-vec
- 長さとキャパシティも含めてヒープに置くVec<T>
だが、未定義動作の可能性があり、怪しい。多分tinyvec
かsmallvec
を使ったほうが良い。
-
anyhowは英語で「とりあえず」「とにかく」などの意味を持つ。 ↩
-
f32
やf64
がEq
を実装していないのは、NaN == NaN
がfalse
を返すためである。NaN
である可能性を排除すれば、それ以外の値a
においてa == a
がtrue
になるので、Eq
をderive
できる。 ↩ -
以下の場合は
pcre
クレートを使わなくても目的が達成できる:-
\Q
...\E
はエスケープシーケンスを部分的に無効化するシンタックスシュガー。 -
\cX
はCtrlとXキーを押したときに出力される文字のシンタックスシュガー。 - Unicodeプロパティの
\p
とその否定版の\P
はregex
クレートでサポートされている。 - 貪欲ではない繰り返し演算子
??
、+?
、*?
はregex
クレートでサポートされている。 -
\e
は\x1B
のシンタックスシュガー。 -
\U{hex}
はRust自体のエスケープシーケンスとして使える。
-
-
コンパイルエラーでもこのクレートに誘導される。 ↩
-
「アホ」ではない。 ↩