Summary
#[derive(::prost::Message)] currently sorts message fields by tag before generating several code paths. That is correct for encoding-related codegen, but for tuple structs the generated Default impl also uses that tag-sorted field list.
For tuple structs, constructor argument order must follow Rust declaration order, not protobuf tag order. When those differ, the generated Default impl can either:
- fail to compile because the constructor arguments are emitted in the wrong positions, or
- compile but assign defaults to the wrong tuple element when multiple fields share the same Rust type.
Reproduction
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct T(
#[prost(uint32, tag = "2")] pub u32,
#[prost(string, tag = "1")] pub ::prost::alloc::string::String,
);
Then run:
Expected behavior
The derive should preserve declaration order for tuple constructor arguments and generate something equivalent to:
impl ::core::default::Default for T {
fn default() -> Self {
T(
0u32,
::prost::alloc::string::String::new(),
)
}
}
This should compile regardless of whether protobuf tags match tuple-field declaration order.
Actual behavior
The generated tuple Default constructor is built in tag order instead of declaration order, effectively producing the arguments in the wrong positions.
For the example above, the generated constructor is equivalent to:
T(
::prost::alloc::string::String::new(),
0u32,
)
which causes a compile error similar to:
error[E0308]: arguments to this struct are incorrect
expected `u32`, found `String`
expected `String`, found `u32`
Impact
- Valid tuple structs deriving
Message can fail to compile when tags are out of declaration order.
- In some cases this can become a silent semantic bug instead of a compile error. For example, two tuple fields may share the same Rust type while still having different defaults at the protobuf layer (for example distinct enum-typed scalar fields, both represented as
i32). In that case the generated Default impl can compile while assigning defaults to the wrong slots.
Summary
#[derive(::prost::Message)]currently sorts message fields by tag before generating several code paths. That is correct for encoding-related codegen, but for tuple structs the generatedDefaultimpl also uses that tag-sorted field list.For tuple structs, constructor argument order must follow Rust declaration order, not protobuf tag order. When those differ, the generated
Defaultimpl can either:Reproduction
Then run:
Expected behavior
The derive should preserve declaration order for tuple constructor arguments and generate something equivalent to:
This should compile regardless of whether protobuf tags match tuple-field declaration order.
Actual behavior
The generated tuple
Defaultconstructor is built in tag order instead of declaration order, effectively producing the arguments in the wrong positions.For the example above, the generated constructor is equivalent to:
which causes a compile error similar to:
Impact
Messagecan fail to compile when tags are out of declaration order.i32). In that case the generatedDefaultimpl can compile while assigning defaults to the wrong slots.