-
-
Notifications
You must be signed in to change notification settings - Fork 7.4k
Description
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: falseThe 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] = FalseDescribe 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
-
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. -
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.