oxidecomputer / oxidecomputer/typify

Non-required property with a `default` is not an `Option`

Open
#319 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
898
Forks
114
Avg merge
4h 18m
Merged PRs (30d)
14

Description

Given

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "Data Package",
  "type": "object",
  "properties": {
    "encoding": {
      "propertyOrder": 100,
      "type": "string",
      "default": "utf-8"
    }
  }
}

cargo-typify 0.0.13 outputs (among other items) this struct def:

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct DataPackage {
    #[serde(default = "defaults::data_package_encoding")]
    pub encoding: String,
}

Checking the README I saw the note:

Properties that are not in the required set are typically represented as an Option<T> with the #[serde(default)] attribute applied. Non-required properties with types that already have a default value (such as a Vec<T>) simply get the #[serde(default)] attribute (so you won't see e.g. Option<Vec<T>>).

My hunch is that the thinking was "During deserialization, such a struct field will surely be Some<T>, so just type it as T". That makes sense if one is only deserializing; OTOH it makes it impossible to manually instantiate a certain class of conforming struct: one which, when serialized, would lack the encoding property. I see two workarounds for myself:

  • Specify a value for the encoding field, e.g., whatever the generated defaults::data_package_encoding function returns. Consumers of the JSON documents I'm creating may infer that value anyway, given the default in the JSON Schema.
  • Remove the default from the schema I'm inputting into typify

In my particular situation I've sought to create JSON documents that lack this property since it happens to be somewhat misleading/a bad fit for my use case. But since consumers of my JSON might infer a value anyway, maybe I should just not claim that my JSON conforms to this schema. I'll probably do that no matter what.

Seems like two potential goals are in tension: Being able to construct any valid instance, vs. having deserialized structs be maximally ergonomic. If the former's more important to you, might it make sense to drop the special-casing of "non-required properties with defaults", and treat their typing the same as other non-required properties?

(caveat: This is my second week seriously writing Rust so thank you for your patience if I got anything wrong in this or other issues!)

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the cargo-typify generation path and compare its output for a non-required property with a default against the README guidance and the JSON Schema example. Define whether generated types must represent both presence and omission, then add regression coverage for the resulting serialization and deserialization behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
32/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.