[GH-ISSUE #2902] ResolverOpts Deserialize impl doesn't respect ResolverOpts::Default #1083

Closed
opened 2026-03-16 01:34:24 +03:00 by kerem · 1 comment
Owner

Originally created by @wez on GitHub (Apr 3, 2025).
Original GitHub issue: https://github.com/hickory-dns/hickory-dns/issues/2902

When Deserializeing an empty or partial json object into ResolverOpts, fields such as recursion_desired will default to the values of the individual field types rather than the values produced by ResolverOpts::default() because serde can only see the individual fields and not the container.

In the case of recursion_desired serde will produce a default value of false because that is the default value of a bool, but ResolverOpts::default would have set it to true.

This can lead to surprising behavior unless the object is fully filled out!

You could argue that this is an ergonomics issue and that the value being deserialized should be fully populated.

In my application we allow our users to configure the resolver like this, with the expectation that any unspecified fields will use reasonable defaults:

kumo.on('init', function()
  kumo.dns.configure_resolver {
    name_servers = {
      '10.0.0.1:53',
    },
    -- this options field is deserialized as ResolverOpts
    options = {
      edns0 = true, 
      use_hosts_file = "Auto",
    },
  }
end)

In this situation we don't have an ergonomic way to produce the fully specified object.

We could potentially define our own intermediate type to handle this, but it is undesirable to maintain it, especially because the schema of that type was recently changed in updating to hickory 0.25.

The approach that we use to handle this situation in our own types is to define initialization functions for each of the fields where the default needs to be different from the type's own default:

struct EgressPathConfig {
    #[serde(default = "EgressPathConfig::default_enable_rset")]
    pub enable_rset: bool,
}
impl Default for EgressPathConfig {
   fn default() -> Self {
      Self {
        enable_rset: Self::default_enable_rset(),
      }
   }
}

impl EgressPathConfig {
   fn default_enable_rset() -> bool {
       true
   }
}

I think it would be great if hickory could do something similar for the fields that are sensitive to this!

Originally created by @wez on GitHub (Apr 3, 2025). Original GitHub issue: https://github.com/hickory-dns/hickory-dns/issues/2902 When `Deserialize`ing an empty or partial json object into `ResolverOpts`, fields such as `recursion_desired` will default to the values of the individual field types rather than the values produced by `ResolverOpts::default()` because serde can only see the individual fields and not the container. In the case of `recursion_desired` serde will produce a default value of `false` because that is the default value of a `bool`, but `ResolverOpts::default` would have set it to `true`. This can lead to surprising behavior unless the object is fully filled out! You could argue that this is an ergonomics issue and that the value being deserialized should be fully populated. In my application [we allow our users to configure the resolver](https://docs.kumomta.com/reference/kumo.dns/configure_resolver/) like this, with the expectation that any unspecified fields will use reasonable defaults: ```lua kumo.on('init', function() kumo.dns.configure_resolver { name_servers = { '10.0.0.1:53', }, -- this options field is deserialized as ResolverOpts options = { edns0 = true, use_hosts_file = "Auto", }, } end) ``` In this situation we don't have an ergonomic way to produce the fully specified object. We could potentially define our own intermediate type to handle this, but it is undesirable to maintain it, especially because the schema of that type was recently changed in updating to hickory 0.25. The approach that we use to handle this situation in our own types is to define initialization functions for each of the fields where the default needs to be different from the type's own default: ```rust struct EgressPathConfig { #[serde(default = "EgressPathConfig::default_enable_rset")] pub enable_rset: bool, } ``` ```rust impl Default for EgressPathConfig { fn default() -> Self { Self { enable_rset: Self::default_enable_rset(), } } } impl EgressPathConfig { fn default_enable_rset() -> bool { true } } ``` I think it would be great if hickory could do something similar for the fields that are sensitive to this!
kerem closed this issue 2026-03-16 01:34:29 +03:00
Author
Owner

@djc commented on GitHub (Apr 7, 2025):

This is important for us too, because we expose this in our TOML config as well, which would suffer from the same issues.

Submitted a PR in #2913.

<!-- gh-comment-id:2783800363 --> @djc commented on GitHub (Apr 7, 2025): This is important for us too, because we expose this in our TOML config as well, which would suffer from the same issues. Submitted a PR in #2913.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/hickory-dns#1083
No description provided.