こんばんは。本稿では自分が個人的な観点で便利なクレート、そうではないクレートを分類していきたいと思います。なるべく主観的な観点を排除して、客観的な観点からpros/consを記述できるように心がけますが、そうなっていない場合でもご了承ください。
Goals
- 一般に役に立つとされるクレートの(非)網羅的なリストを作ること
- もはや時代遅れとみなされるクレートの(非)網羅的なリストを作ること
Non goals
- FFIバインディングはキリがないので比較用に示す場合以外は基本的に取り上げない。これらのクレートを探したいときはcrates.ioで
external-ffi-bindingsカテゴリを探索すること。既存のクレートが存在しない場合は自分で作ることもできるが、やはりこの記事のスコープを超える。
Tier A
特定の用途において絶対外せない。
シリアライズ・デシリアライズ
-
serde- Rustのシリアライズ・デシリアライズといえばこれといっていいほど非常によく見かける。derivefeatureを有効にするとシリアライズ・デシリアライズの実装が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を自己再帰できるようにする。 -
tokio5 - 言わずと知れた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_genfeatureを有効にすることが推奨される (無効のままでも動くが、配列の長さが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と同じコマンドラインフラグの定義をできるクレートだが、メンテナンスモードに入っている。clap3.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自体のエスケープシーケンスとして使える。
-
-
コンパイルエラーでもこのクレートに誘導される。 ↩
-
「アホ」ではない。 ↩