Preview of the proposed up-rust native-frame-model branch (up-rust b6b99c6d, up-spec f0e9b17) — not released documentation. branch · write-up

Module owned

Source
Expand description

§Owned frames (experimental)

The owned-frame family (owned-frame-transport) exchanges whole validated frames as owned values — and, the headline it earns over the UTransport family: owned frames ride the selected-wire system. Custom wire encodings, negotiated wire identities, native-prefix metadata — the machinery the wire chapter describes — compose over owned carriage, while the UTransport path’s encoding stays whatever the transport does internally. Pick this family when you want either of two things: pluggable wire formats, or validation done once at the boundary when your technology naturally hands over complete buffers (a DDS sample, a queue element).

You implement UOwnedTransportImpl; users call UOwnedTransport and receive through UOwnedListener. The shape mirrors the UTransport family with frames in place of messages — and validity is in the type: UOwnedFrame written plain means UOwnedFrame<Validated>, the only state a transport ever sees.

use up_rust::{UOwnedFrame, UOwnedTransportImpl, UStatus};

struct MyTransport { /* connection handle */ }

#[async_trait::async_trait]
impl UOwnedTransportImpl for MyTransport {
    async fn send_validated_owned(&self, frame: UOwnedFrame) -> Result<(), UStatus> {
        // `frame` is `UOwnedFrame<Validated>` by type: metadata checked,
        // payload presence consistent — no re-checking here.
        self.backend().publish(frame.metadata(), frame.payload())
    }
    // register/unregister owned listeners: same registry pattern as the
    // UTransport tutorial.
}

Building a frame is one step — constructors validate, so what you get back is already the state dispatch requires:

use up_rust::{PayloadEncoding, UMessageBuilder, UOwnedFrame, UUri};

let topic = UUri::try_from_parts("demo", 0x1_0001, 1, 0x8001)?;
let message = UMessageBuilder::publish(topic).build()?;

// Frame metadata is a projection of the same attributes a UMessage carries:
let metadata = message.attributes().to_frame_metadata(PayloadEncoding::RAW)?;
let frame = UOwnedFrame::with_payload(metadata, b"reading".to_vec())?;

assert_eq!(frame.payload_bytes(), b"reading");

The other state exists for the receive side: bytes decoded off the wire become UOwnedFrame<Unvalidated>, and the only way forward is UOwnedFrame::validate, which transitions to Validated. The state transition is the validation; an invalid frame reaching a listener is a compile error, not a runtime drop.

When to choose owned over zero-copy: your technology hands you complete, already-copied buffers anyway, so loan mechanics buy nothing — but wire pluggability and boundary validation still do.

If your technology should instead stay ignorant of frames entirely and carry pre-encoded bytes, implement UOwnedTransportCore and let UWireTransport compose the encoding above you — the same composition story as the zero-copy core, and the wire chapter’s composition section shows the payoff: the transport code is identical for every wire.

Shared obligations: the transport hub. Which trait is which: the trait map.