Expand description
RFC-0088 / phase-421 W1 — the compile-time message-format check.
Gated exactly like session, whose IMAGE_SERIALIZATION_FORMAT_ID it
compares against: with no RMW seam compiled in there is no backend, so
there is no format for a message to disagree with.
RFC-0088 / phase-421 W1 — the message-format check, as a compile error.
ROS 2 names its serialization format with a string and answers
rmw_get_serialization_format() at run time, because
rosidl_typesupport_c resolves the format’s implementation through
dlopen and the string is the linker key. nano-ros links one image and
selects its backend by cargo feature, so the same question has a
compile-time answer: crate::session::IMAGE_SERIALIZATION_FORMAT_ID.
[assert_message_format] compares a message’s declared
nros_core::RosMessage::SERIALIZATION_FORMAT_ID against that constant inside an
inline const {} block. The comparison therefore happens during
monomorphisation of the entity-creation call, and costs nothing at run
time — no branch appears on the publish path, which is the property
RFC-0088 D1 asks for.
§What the error looks like
Creating a publisher for a Uorb message in an image whose backend speaks
CDR fails like this. The primary span is the assert! below and the
offending type + call site arrive as notes, which is how rustc reports a
post-monomorphisation const failure:
error[E0080]: evaluation panicked: message serialization format does not
match the linked backend (RFC-0088)
--> packages/core/nros-node/src/format_check.rs:83:9
| evaluation of `format_check::assert_message_format::<UorbProbe>::{constant#0}`
| failed here
note: the above error was encountered while instantiating
`fn assert_message_format::<UorbProbe>`
--> src/main.rs:12:9
|
12 | node.create_publisher::<VehicleStatus>("/status")?;It is a cargo build error, not a cargo check one. An inline const
block in a generic function is evaluated by the monomorphisation collector,
which only runs during codegen — cargo check -p nros-node compiles the
mismatch silently. Measured 2026-09-04. just ci gate catches it because
test-unit builds; a lane that only type-checks does not.
§Coverage
The check reads nros_core::RosMessage::SERIALIZATION_FORMAT_ID, and
MessageForRmw — the bound every typed creator carries — requires
RosMessage under every backend. So the assertion is universal: zenoh,
XRCE, Cyclone and uORB alike.
Keying it on nros_serdes::schema::Message instead would have covered only
Cyclone, because MessageForRmw requires a schema solely under
cfg(rmw_needs_type_descriptors) — and would therefore have been absent
under uORB, the one backend whose format differs and the reason the check
exists. The const is defaulted rather than required for the reason
phase-380 W4 recorded: tightening the message contract to serve a build
assertion broke examples/native/rust/custom-msg, the documented
hand-written-message pattern. A default costs those implementors nothing.
§Proving the negative case
A compile error cannot be asserted by a running test, and this workspace has
no trybuild (or any compile-fail) harness; adding one for a single case is
more machinery than the case is worth. Reproduce it by hand instead —
append to this file:
fn _mismatch() {
struct UorbProbe;
impl nros_core::RosMessage for UorbProbe {
const SERIALIZATION_FORMAT_ID = nros_serdes::format::SerializationFormatId::Uorb;
const TYPE_NAME: &'static str = "px4/msg/UorbProbe";
const TYPE_HASH: &'static str = "";
}
assert_message_format::<UorbProbe>();
}The probe must be pub (or otherwise reachable): a private, never-called
function is dropped before the monomorphisation collector runs, and the
assertion then never instantiates — measured, having first written the
probe private and seen a clean build.
and cargo build -p nros-node --features rmw-cffi reports the E0080
above (cargo check does not — see above). The runnable half
of the claim is tests::cdr_and_uorb_are_distinguishable: if the two
formats ever stopped differing, the compile error would stop being
reachable and every assertion in the tree would pass vacuously.
Functions§
- assert_
message_ format - Assert at compile time that
Mis encoded in the format the linked backend speaks. - assert_
raw_ format - The raw-entity counterpart: assert that
F, the format a caller states its already-encoded bytes are in, is the one the linked backend speaks.