つまり、デフォルト指定してても、いざデフォルトで取得してしまうと、意図した値がデフォルトにならない。
何言ってるんやって感じなので、順を追って書く。
default = "path"属性とは
Field attributes · Serde https://serde.rs/field-attrs.html
SerdeでstructをJSONから生成するとき、つまりdeserializeするとき、オプショナルなキーなんかに初期値を設定したいとする。
その場合には、#[serde(default)]という属性を書けばよい。
#[derive(Deserialize)]
struct Config {
#[serde(default)]
key: String,
}
ただこの指定では、その型の初期値しか指定できない。
Stringなら空文字列boolならfalse- 数値型は
0 - etc…
そこで、デフォルトをtrueにしたいとか、独自の文字列にしたい場合には、#[serde(default = "path")]と書きつつ、pathという関数を定義しておく。
serde::Deserializeで、default=trueしたい | Memory ice cubes https://leaysgur.github.io/posts/2024/03/13/213641
deserialize()では呼ばれるけど
で、この#[serde(default = "path")]による指定は、deserialize()される時に呼ばれる。
let config: Config = serde_json::from_str(json).unwrap();
// or
let config = Config::deserialize(json).unwrap();
// or...
このときは、意図した通りにカスタムな初期値が指定されてる。
default()では呼ばれない
これが問題のケース。
ついでに#[derive(Default)]属性を付けて、default()できるようにしてた場合。
let config = Config::default();
// or...
なんとこのときは、さっきのカスタムな初期値は指定されない。
バグでは・・・?って思ったけど、どうやら意図通りらしい。
Add #[derive(serde::Default)] that is aware of serde
defaultannotations · Issue #2622 · serde-rs/serde https://github.com/serde-rs/serde/issues/2622
deserialize()で呼ばれても、ネストの影響を受ける
つまりこういうこと。
use serde::Deserialize;
#[derive(Debug, Deserialize, Default)]
struct Config {
#[serde(default)]
k1: bool,
#[serde(default = "default_true")]
k2: bool,
#[serde(default)]
nested: Nested,
}
#[derive(Debug, Deserialize, Default)]
struct Nested {
#[serde(default)]
k1: bool,
#[serde(default = "default_true")]
k2: bool,
}
fn default_true() -> bool {
true
}
fn main() {
let c = Config::deserialize(serde_json::json!({})).unwrap();
println!("{c:?}");
let c = Config::default();
println!("{c:?}");
}
これを実行すると、こうなる。
Config { k1: false, k2: true, nested: Nested { k1: false, k2: false } }
Config { k1: false, k2: false, nested: Nested { k1: false, k2: false } }
前者はc.nested.k2がfalseになってしまってて、後者はc.k2ですらfalseになってしまってる。
どうする
先のIssueでは、これを解決するためのクレートを作ってる人がいた。
BrynCooke/serde-derive-default https://github.com/BrynCooke/serde-derive-default
これで済ませられるならそれでいい気もするけど、正解はわかってない。
自分でDefaultを実装するのが、正攻法なんだろうか。