Skip to content

[Rust] Support default values for model properties #22455

@ThomasVille

Description

@ThomasVille

Is your feature request related to a problem? Please describe.

The Rust client generator (rust) does not currently support the default schema element for model properties. When a property has a default value defined in the OpenAPI specification, the generated Rust model ignores it and falls back to Rust's #[derive(Default)], which uses None for Option<T> types.

For example, the petstore.yaml spec defines the Order schema with complete: default: false:

Order:
  type: object
  properties:
    id:
      type: integer
      format: int64
    petId:
      type: integer
      format: int64
    quantity:
      type: integer
      format: int32
    shipDate:
      type: string
      format: date-time
    status:
      type: string
      description: Order Status
      enum:
        - placed
        - approved
        - delivered
    complete:
      type: boolean
      default: false

The generated Rust model ignores this default:

#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct Order {
    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
    pub id: Option<i64>,
    #[serde(rename = "petId", skip_serializing_if = "Option::is_none")]
    pub pet_id: Option<i64>,
    #[serde(rename = "quantity", skip_serializing_if = "Option::is_none")]
    pub quantity: Option<i32>,
    #[serde(rename = "shipDate", skip_serializing_if = "Option::is_none")]
    pub ship_date: Option<String>,
    #[serde(rename = "status", skip_serializing_if = "Option::is_none")]
    pub status: Option<Status>,
    #[serde(rename = "complete", skip_serializing_if = "Option::is_none")]
    pub complete: Option<bool>,
}

This means Order::default().complete returns None instead of Some(false), and deserializing JSON with a missing complete field also yields None.

In contrast, the Python generator correctly handles this default:

complete: Optional[StrictBool] = False

Describe the solution you'd like

Generate a custom Default implementation and add #[serde(default)] at the struct level when any property has a default value:

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(default)]
pub struct Order {
    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
    pub id: Option<i64>,
    #[serde(rename = "petId", skip_serializing_if = "Option::is_none")]
    pub pet_id: Option<i64>,
    #[serde(rename = "quantity", skip_serializing_if = "Option::is_none")]
    pub quantity: Option<i32>,
    #[serde(rename = "shipDate", skip_serializing_if = "Option::is_none")]
    pub ship_date: Option<String>,
    #[serde(rename = "status", skip_serializing_if = "Option::is_none")]
    pub status: Option<Status>,
    #[serde(rename = "complete", skip_serializing_if = "Option::is_none")]
    pub complete: Option<bool>,
}

impl Default for Order {
    fn default() -> Self {
        Self {
            id: None,
            pet_id: None,
            quantity: None,
            ship_date: None,
            status: None,
            complete: Some(false),
        }
    }
}

The #[serde(default)] attribute ensures that during deserialization, missing fields are populated from Order::default() rather than using None.

Describe alternatives you've considered

  1. Per-field #[serde(default = "fn_name")]: This works but requires generating a separate function for each field with a default, leading to more verbose output. The struct-level #[serde(default)] approach is cleaner.

  2. Post-processing the generated code: Users could manually edit the generated models, but this breaks on regeneration and defeats the purpose of code generation.

Additional context

Type mapping for defaults:

Type OpenAPI default Rust default expression
string "foo" Some("foo".to_string())
integer 42 Some(42)
number 1.234 Some(1.234)
boolean false Some(false)
enum placed Some(Status::Placed)
array ["a", "b"] Some(vec!["a".to_string(), "b".to_string()])

Related issues:

  • #3359 - [ASP.NET Core] Support default values for model properties
  • #14013 - [go-server] enforcement of minimum, maximum; application of default

I'd be happy to work on a PR to implement this feature.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions