`#[serde(default)]` versus `impl Default`

I have a large struct with various configuration values in it, something like:

struct Style {
    foreground: f32,
    background: f32,
    size: [f32; 2],
    ...etcetcetcetc...
}

I have defined the defaults for these using Default, like so:

impl Default for Style {
    fn default() -> Self {
        Self {
            foreground: 0.4,
            background: 0.8,
            ...etc...
    }
}

I now want to load this from a file, allowing any of the fields to by omitted - and if they are omitted it just uses the default value.

So using serde_derive I did this:

#[derive(Deserialize, Serialize)]
struct Style {
    #[serde(default)]
    foreground: f32,
    #[serde(default)]
    background: ...

However the #[serde(default)] annotation uses the default value of a f32 (which is 0.0), so if I load an empty document I'll get a bunch of 0.0 instead of the values from the impl Default

I'm doing something like this:

let style = if let Some(path) = cli_options.override_settings {
    let s: Style = serde_whatever.load(path).unwrap();
} else {
    Style::default()
};

The problem is how to define the default values once, for both the serde-loading and non-serde code paths?

Things I've thought of are:

  1. #[serde(default="...")] seems to require passing a function, which would then mean one function for each of the many struct entries which is impractical
  2. I could define each of the Style entries as Option<f32> etc. That way the default is None - then I can do let mut style = Style::default(); and merge the loaded settings with style.update(serde_whatever.load(path)); - this would work okay, but requires wrapping everything in Option which is wrong (as the values aren't optional, and would thus require more clutter like style.foreground.unwrap())

I think you'll have to define a function for each unfortunately.

You can apply #[serde(default)] directly on the struct instead of the fields (source).

When deserializing, any missing fields should be filled in from the struct's implementation of Default. Only allowed on structs.

7 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.