Skip to content

docs: say what actually guards the dynamic_message publish/take casts - #665

Open
espressolee wants to merge 1 commit into
ros2-rust:mainfrom
espressolee:docs/dynamic-message-safety-comments
Open

docs: say what actually guards the dynamic_message publish/take casts#665
espressolee wants to merge 1 commit into
ros2-rust:mainfrom
espressolee:docs/dynamic-message-safety-comments

Conversation

@espressolee

@espressolee espressolee commented Aug 8, 2026

Copy link
Copy Markdown

Comments only. No code changes — every changed line starts with //, and stripping comments
from both files before and after gives byte-identical text.

What it fixes

DynamicPublisher::publish carries this:

// SAFETY: The message type is guaranteed to match the publisher type by the type system.

It is a verbatim copy of the comment on Publisher::publish, misspelling of "explictly"
included. On Publisher<T> the sentence is true — the generic parameter ties the message to
<T as Message>::RmwMsg. On DynamicPublisher, whose whole point is that the type is a runtime
value, the compiler cannot see anything of the sort. What actually guards the cast is the
message_type comparison two lines above, and that comparison is by name
(MessageTypeName { package_name, type_name }).

The obligation neither comment mentions

Two type support libraries are involved:

library role
DynamicMessageMetadata rosidl_typesupport_introspection_c lays out message.storage
the publisher / subscription rosidl_typesupport_c what rcl_publish / rcl_take serialise with

So the name comparison rests on those two describing the same layout. Both constructors already
say the two libraries exist, in prose a few lines up — "This loads the introspection type
support library … However, we also need the regular type support library"
— so this is known,
just missing from the place where the unsafe block is justified.

DynamicSubscription's take() is the mirror: metadata.create() lays out the storage by
introspection, rcl_take writes it through the C type support, and the comment discusses
pointer validity only.

Both are sound as written

Each constructor resolves the metadata and the C type support from the same package name in the
same function, through one ament.find_package, so they come from one install prefix and agree
by construction. This PR records that, rather than changing it. The point is that a reader
auditing these blocks is currently told the compiler is checking something it never sees.

Happy to reword or drop either hunk.


Generative AI disclosure

This PR was opened on 2026-08-08, before this repository adopted the OSRF AI policy in #672 on
2026-09-01. Adding the disclosure now so the record is complete:

Assisted-by: Codex:gpt-5.6-sol

AGENTS.md also asks for the trailer in the commit message body. I have left the commit as it
is rather than rewriting it, since amending would invalidate the existing CI results and any
references to the current SHA. Happy to amend and force-push if you would prefer the trailer on
the commit itself.

The SAFETY comment on DynamicPublisher::publish is a verbatim copy of the one
on Publisher::publish, typo included. On Publisher<T> its first sentence is
true: the generic parameter ties the message to <T as Message>::RmwMsg. On
DynamicPublisher, whose whole point is that the type is a runtime value, it
claims a guarantee the type system cannot give. The actual guard is the
message_type comparison two lines above, and that comparison is by name.

Neither comment mentions the obligation that the name comparison rests on:
two different type support libraries are involved. DynamicMessageMetadata
loads rosidl_typesupport_introspection_c and that is what lays out
message.storage; rcl_publish serialises those bytes through
rosidl_typesupport_c. Both constructors already say so in prose a few lines
up -- "This loads the introspection type support library ... However, we also
need the regular type support library" -- so the fact is known, just absent
from where the unsafe block is justified.

Same for DynamicSubscription: metadata.create() lays the storage out by
introspection, rcl_take writes it through the C type support, and the comment
discusses pointer validity only.

Both are sound as written: each constructor resolves the metadata and the C
type support from the same package name in the same function, so they come
from one install prefix. This records why, so a reader auditing these blocks
is not told the compiler is checking something it never sees.

Comments only; no code changes.
@espressolee

Copy link
Copy Markdown
Author

Gentle review ping: this remains a comments-only safety-contract clarification at 15b5548b24b0db4161d46a901aab36c6324f01f0.

  • Rust Stable: success
  • Rust Minimal: success
  • Rust Windows: success
  • mergeable: true
  • no runtime, ABI, or generated-code change

The issue it documents (#664) is still open; this PR only states the existing ownership/lifetime guard around the casts and does not claim to fix #664. Happy to reword or drop either comment hunk.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant