Skip to main content

Module format_check

Module format_check 

Source
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 M is 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.