serdeでは様々な入力をenumでまとめて扱うことができます。
serdeのマニュアルに載っているので、このリンクを読んだことがあるなら既知の情報が多いかもしれません。
以下のようなMessageをjsonで受け取ることを例として見ていきます。
#[derive(Serialize, Deserialize)]
enum Message {
A { id: u64, event_code: u64 },
B { id: u64, message: String },
}
Externaly tagged
何の属性を設定しない、一般的なenumのシリアライズ方法です。
variantの名前をkeyに、中身をvalueに設定します。
期待する入力は以下のようなjsonになります。
{
"A" : {
"id" : 1,
"event_code": 4
}
}
Internally tagged
対象のenumに#[serde(tag = "type")]
を付けるとvariantの名前をvalueとして受け取ることができます。keyはtagの後につけた"type"の場所の値になります。
{
"type" : "A",
"id" : 1,
"event_code": 4
}
Adjacently tagged
対象のenumに#[serde(tag = "type", content = "message")]
を付けると中身もobjectとして受け取ることができます。
{
"type" : "A",
"message" : {
"id" : 1,
"event_code": 4
}
}
Untagged
対象のenumに#[serde(untagged)]を付けるとtagではなくkeyの種類でenumを変化させます。keyで合致した最初のvariantを返します。
{
"id" : 1,
"event_code": 4
}
Untaggedの使い方
Untaggedは受け取る型を任意にするために使うことができます。
// 文字列でも数値でも受け取れる型
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
enum Input{
Str(String),
Int(i64),
}
比較
ここまではリファレンス通りで寂しいのでいろいろ比較してみました。
expand後のサイズ
まずはcargo expand --release
してコードのサイズがどれくらい違うかを比較します。
serdeの属性以外は同じコードにします。
以下のenumで比較します。
enum Message {
A { id: u64, event_code: u64 },
B { id: u64, message: String },
}
Externally | Internally | Adjacently | Untagged | |
---|---|---|---|---|
行数 | 759行 | 750行 | 1383行 | 540行 |
バイト数 | 40KB | 36KB | 84KB | 25KB |
Untaggedが一番多いとの個人的予想を裏切り、Adjacentlyが一番長いという結果に。
ベンチマーク
同様に同じenumでパフォーマンスを比較してみました。
上と同じMessageで比較しました。
cargo criterionでのベンチマークの結果は以下のようになりました。
Externally | Internally | Adjacently | Untagged | |
---|---|---|---|---|
Message | 69.486 ns | 232.36 ns | 130.55 ns | 141.51 ns |
Externallyが早いですがこれくらいのenumならパフォーマンス上大差がないのがわかります。(後で大きなサイズのenumでどれくらい大きくなるかも比較してみたいと思います。)
まとめ
serdeのderiveがいろいろうまくやってくれるのでいろんなjsonの表現からenumを作ることができます。serdeのリファレンスはいろいろ便利な属性が載っているのでぜひご覧ください。