Rust Bindings Implementation Blueprint¶
What this chapter defines — the Rust crate's public shape and the
contracts/runtimesource placement rules.
This document defines the Rust crate shape. It is not an
exhaustive list of every public item. The concrete public contract source
is bindings/rust/src/contracts/. bindings/rust/src/lib.rs is the crate
projection that re-exports the intended public API.
The contracts source tree, private runtime tree, public export projection, rustdoc, tests, samples,
perf runner, and runtime project core/include/zlink.h capabilities into an idiomatic Rust API. This
document defines where each public contract and runtime helper is owned.
This binding follows
This binding follows the common bindings architecture map using Rust
naming conventions. The contracts and private runtime modules organize
source ownership, and lib.rs decides which module paths become the
public crate API.
| Section | Covers |
|---|---|
| Public contract source | The contracts/runtime/native source locations and documentation's role |
| Repository layout | The file-granularity policy and the aligned directory tree |
| API change principles | Public mapping and contract/runtime boundary requirements |
| Library shape | RAII ownership, Result, Builders, the unsafe boundary |
| Contract / runtime placement rules | The boundary between public declarations and private helpers |
| Contract file layout | Files per category under contracts/ |
| Runtime file layout | Files per category under runtime/ |
| Creation entry points | The list of crate root constructors and contract methods |
| Contract category map | Category → module mapping |
| Standard interface rules | recv signatures, builder start methods, naming constraints |
| Crate layout | The classification of public modules |
| Byte HWM and Auto-HWM | Mapping between Rust u64 and Core byte HWM |
| Receive flow state | The receive-flow state type, setter, and monitor surface |
| Required capability coverage | The user-facing capabilities that must be guaranteed once aligned |
| Spot Get-Or-Create | The get_or_create_spot contract |
| Receive and Subscribe shape | Storage reuse, distinguishing no-data |
| Error and validation policy | FFI-boundary validation and type-based errors |
| Performance policy | Hot-path constraints |
| Implementation checklist | What to confirm before declaring alignment, and required verification commands |
| Actor and Spot Route results | The route-result value types and Actor-directed send/request |
Public contract source¶
- Public contract source:
bindings/rust/src/contracts/. - Crate projection:
lib.rs's public re-exports and the rustdoc for public modules. - Runtime implementation: private modules under
bindings/rust/src/runtime/. - Native bridge: private modules under
bindings/rust/src/runtime/native/— raw handles, callback trampolines, request-progress helpers, and whole-message array helpers. - Concrete crate-private resource storage lives in
bindings/rust/src/internal.rs. Contract files may reference this storage's types but never import runtime resource types directly. FFI declarations and native calls stay underruntime/. - Documentation's role: this README defines the shape and semantic coverage. The public crate export owns the exact member list. Each public item must still map to one of the common contract categories.
Applications, perf, and samples do not depend on private modules or raw FFI bindings.
Repository layout¶
Use these paths consistently when changing the Rust binding.
- Public contract:
bindings/rust/src/contracts/. - Crate projection:
bindings/rust/src/lib.rs. - Runtime implementation: private modules under
bindings/rust/src/runtime/. - crate-private concrete storage:
bindings/rust/src/internal.rs. - Native bridge/artifacts: private modules under
bindings/rust/src/runtime/native/,bindings/rust/native/,bindings/rust/include/. - Codec distribution scope: follow the common raw payload policy.
- Tests:
bindings/rust/tests/. - Samples:
bindings/rust/samples/. - Perf:
bindings/rust/perf/.
lib.rs's public re-exports must be intentional. A Rust module path
becomes part of the public API the moment it is exported. The contracts
and runtime source trees are just an implementation organization unless
lib.rs explicitly exposes a module. zlink::runtime and a raw native
bridge module are never exposed.
The tree below is the aligned implementation structure. Public structs,
enums, traits, errors, free functions, and builder contracts belong to
contracts/ and are intentionally re-exported from lib.rs. FFI bindings,
native struct mirrors, callback trampolines, request-progress helpers,
marshalling, and unsafe whole-message array handling stay private under runtime/. The
crate-private storage module holds only the concrete state a public
wrapper must own, and neither declares nor calls the FFI surface.
File granularity follows the common policy in ../README.md. Keep one
file per independent public concept, or per tightly coupled
operation/model group. Merge a very small marker trait, callback alias,
enum-only module, or pass-through helper module into the nearest contract
file when doing so makes the public shape easier to read.
bindings/rust/
+-- src/
| +-- lib.rs
| +-- internal.rs
| +-- contracts/
| | +-- core/
| | | +-- context.rs
| | | +-- routing_id.rs
| | +-- messaging/
| | | +-- message.rs
| | | +-- received.rs
| | | +-- topic_message.rs
| | | +-- subscription_event.rs
| | +-- sockets/
| | | +-- socket.rs
| | | +-- pair_socket.rs
| | | +-- dealer_socket.rs
| | | +-- router_socket.rs
| | | +-- pubsub_sockets.rs
| | | +-- stream_socket.rs
| | | +-- socket_options.rs
| | | +-- socket_operations.rs
| | +-- eventing/
| | | +-- monitor.rs
| | | +-- poller.rs
| | +-- service/
| | | +-- spot/
| | | | +-- spot_node.rs
| | | | +-- spot.rs
| | | | +-- actor.rs
| | | | +-- spot_operations.rs
| | | | +-- spot_models.rs
| | +-- errors/
| | | +-- errors.rs
| | | +-- results.rs
| +-- runtime/
| | +-- messaging/
| | | +-- message.rs
| | | +-- domain.rs
| | | +-- request_progress.rs
| | +-- sockets/
| | | +-- socket_base.rs
| | | +-- pair_socket.rs
| | | +-- dealer_socket.rs
| | | +-- router_socket.rs
| | | +-- pub_socket.rs
| | | +-- sub_socket.rs
| | | +-- xpub_socket.rs
| | | +-- xsub_socket.rs
| | | +-- stream_socket.rs
| | +-- eventing/
| | | +-- poller.rs
| | | +-- timer.rs
| | +-- service/
| | | +-- spot/
| | | | +-- spot_node.rs
| | | | +-- spot.rs
| | | | +-- actor.rs
| | +-- errors/
| | | +-- native_errors.rs
| | +-- native/
| | | +-- native.rs
+-- tests/
+-- samples/
+-- perf/
+-- native/
+-- include/
The public consumer projection is lib.rs. Tests, samples, and perf use
the public crate projection. If an item is publicly re-exported, a
reviewer must be able to point to the common contract category it belongs
to. If a module exists only for native calls or to maintain an unsafe
invariant, it stays private under runtime/.
API change principles¶
The public projection of a Core capability follows these principles.
- Choose the common contract category that will own the public behavior.
- Add the public type, method, or function to the module that safely owns it, and update the
lib.rsre-export projection if it must be visible at the crate root. - Add a concrete public type or method first, and add a trait only if a genuinely substitutable behavior is needed.
- Keep
unsafe, raw handles, callback userdata, and whole-message array handling inside private modules. - A fallible operation returns a
Resultcarrying typed error information. - Add a test that uses the public crate projection.
- Update samples and perf only through the public API.
- Run formatting and clippy-style checks where available.
The contract/runtime boundary meets these requirements.
- Move public behavior declarations to
src/contracts/<category>/. - Move native-backed implementations to
src/runtime/<category>/. - Keep unsafe FFI, native loading, raw handles, and callback trampolines under
src/runtime/native/. - Public code constructs resources through a crate-root constructor or contract method.
- Crate exports do not expose a runtime module as public API.
- Deprecated wrappers, duplicate operation-start names, and compatibility-only naming aliases are not part of the public surface.
- Tests, samples, and perf use only the public crate export.
- Rustdoc does not expose private implementation modules as public API.
The following Rust-specific shortcuts are not allowed.
- The
contractsmodules do not re-exportruntimeorruntime::native. - Contract files never import a runtime resource type to describe a public service model.
- A public re-export barrel is never the source of a resource's behavior — declarations are split between a named contract module and a runtime implementation module.
lib.rsexports contract names and constructors, and never exports a runtime module.- Public rustdoc never exposes a runtime implementation module path as a public type.
Library shape¶
This binding should feel like a safe Rust crate on top of the native runtime.
- A public resource type owns its native lifetime and releases the resource via
Drop. - A fallible operation returns
Result<T, ZlinkError>, or a more specific typed result when that improves clarity. - Concrete values — message, routing id, received metadata, topic message, snapshot, option, enum, error — stay concrete types.
- A trait is used only when the caller needs substitutable behavior or a generic bound. Not every concrete handle gets a trait by default.
- A Builder is required for multipart send, publish, request, reply, SPOT, and actor operations, and it hides native state.
unsafeand raw FFI are confined to private modules.
Safe FFI RAII wrapper placement¶
A native-backed Rust resource uses Rust's common safe FFI RAII wrapper
pattern. A public struct handle owns the native lifetime, public
inherent impl methods expose safe Rust operations, and Drop releases
the native resource.
The public inherent impl surface lives in the matching contracts/-owned
file. Even when a method body immediately delegates to a runtime helper,
the public method list must be readable from the contract file. A runtime
module hides C API calls, unsafe blocks, raw handles, downcasts, errno
mapping, and native struct conversion behind pub(crate) helper
functions. A public resource's public methods are never left discoverable
only in the runtime module.
A trait is used only when the caller needs substitutable behavior or a
generic bound. A trait is never created just to make a native-backed
handle look like an interface. For a C-handle wrapper with a single
implementation, prefer the combination of a pub struct in contracts/
with public inherent methods, plus private helpers in runtime/.
Contract / runtime placement rules¶
- Public structs, enums, traits, errors, and builder contracts belong to their matching
contracts/category, and are re-exported fromlib.rswhen public. - A public free function, associated helper function, convenience method, or builder helper method belongs to a public module when the caller can use it directly.
- The public inherent
implblock of a native-backed public resource lives in the contract-owned file; its body may thinly delegate to apub(crate)runtime helper. - Runtime handle owners, the request pump, callback adapters, and whole-message array helpers stay private or
pub(crate). - FFI bindings, raw pointers, native struct mirrors, marshalling helpers, and platform loading code stay inside a private FFI/runtime owner.
lib.rsand public rustdoc modules project the contract categories and never expose a runtime module.- A runtime concrete type is a construction target behind a crate root constructor or contract method.
lib.rsmay import a runtime module only to wire up such a constructor, and public signatures use contract names.
Contract file layout¶
The contract source uses the same classification as the .NET bindings blueprint, with Rust naming conventions. Keeping the same conceptual file grouping lets a developer who knows another binding find the same public concept quickly in Rust too.
core/:context.rs,routing_id.rs, and core option/value files.messaging/:message.rs,received.rs,topic_message.rs,subscription_event.rs, common operation payload types.sockets/: socket types/traits, socket option types, send/request/reply builder contracts, stream packet values, socket flags.eventing/: monitor, monitor event/status, poller, poll event, and timer contracts.service/: SPOT node, Spot, Actor, topology model, service operation builders, under aspot/submodule.errors/: public error types, the result domain, error-code mapping.
Avoid a single consolidated models.rs or a runtime export barrel for
public resource behavior. Small DTO-style structs and enums may be grouped
if they can sit with the contract that gives them meaning, but a
native-backed resource and an operation builder each need a named contract
file.
Runtime file layout¶
The runtime source mirrors the same conceptual categories but contains only implementation.
core/: the context implementation and context option helpers.messaging/: message materialization, request progress, request execution, native buffer conversion helpers.sockets/: the socket base type, the socket kernel, the socket implementation for every socket family, callback adapters, operation implementation types.eventing/: the poller/timer/monitor implementations and event materialization helpers.service/: SPOT node, Spot, Actor, topology, and service operation implementations.errors/: native error conversion and validation helpers.native/: FFI bindings, native loading, raw handles, unsafe boundary code.
A runtime module may import contract types, but a contract module never imports a runtime module. The crate root may instantiate a runtime implementation inside a constructor, but exports the contract name, not the runtime implementation module.
Creation entry points¶
Public creation is provided only through crate root constructors and public contract methods.
Context::new(...)creates the native-backed context implementation.Context::create_pair_socket(),create_dealer_socket(),create_router_socket(),create_pub_socket(),create_sub_socket(),create_xpub_socket(),create_xsub_socket(),create_stream_socket()create the native-backed socket implementations.Context::create_spot_node(...)creates the service-layer implementation.- A
Spothandle is obtained viaSpotNode::create_spot(...),entry_spot(),get_or_create_spot(...), orspot_lookup(...). DirectSpotconstruction is not public. - An Actor handle is created via
SpotNode::create_actor(...). Direct Actor construction is not public. Poller::new(...),Timer::new(...), and the timer-on-SPOT helpers create eventing resources.Polleraccepts a socket monitor as a source throughadd_monitor(&self, monitor: &SocketMonitor, events: i16, slot: usize) -> Result<(), ConfigError>,modify_monitor(&self, monitor: &SocketMonitor, events: i16) -> Result<(), ConfigError>andremove_monitor(&self, monitor: &SocketMonitor) -> Result<(), ConfigError>(common spec "Monitor sources inPoller").SocketMonitordoes not implement the socket-only sealedPollable(this protects theproxyargument contract). OnlyPOLLINis valid for a monitor; any other mask is rejected withConfigError(ConfigResult::InvalidArgument). Drain withrecv_with_flags(RecvFlags::DONT_WAIT)after readiness;PollEventreports it asPollSourceKind::Socket.AtomicCounter::new(),Stopwatch::start(),Thread::start(...)create caller-owned utility resources.- Version and capability lookup, strerror, proxy, sleep, and the multipart cleanup helper are public crate functions. The FFI calls behind these functions stay private.
Contract category map¶
These categories map to lowercase modules under
bindings/rust/src/contracts/, and are the review-ownership map for
public crate items and re-exports.
core/: context, context option, routing id, version/capability lookup helpers, utility contracts.messaging/: message, received metadata, topic message, subscription event, stream packet data, builder payload helpers.sockets/: socket operations, socket family, typed options, request/reply, publish/subscribe surfaces.eventing/: monitor, monitor snapshot/event, poller, poll event, timer, public poll helpers.service/: SPOT node, SPOT handle, topology model, actor ref, actor lifecycle, operation builders.errors/: the typed error/result domain.- Enum, flag, and result types live in the category that defines their meaning. No
enumsmodule is created just to group declarations syntactically.
Standard interface rules¶
- Data-plane
recv, routed recv,subscribe, and subscription-event receive fill a caller-provided&mut Received,&mut TopicMessage, or&mut SubscriptionEventvalue and returnResult<bool, RecvError>. - Send, routed send, publish, request, reply, SPOT operations, and Actor location/session operations return a typestate builder.
- A builder's start method takes only a target identity, topic, channel, routing ID, or
ReplyToken. Payload, request timeout, and terminal choice are the builder's state or stages. - SPOT's channel-targeted operations use
send_to_channel(...)andrequest_to_channel(...). SPOT's topic publish keepspublish(topic)as-is. - No single-payload shortcut method is added under the same name as an operation's start method.
send(message),send(routing_id, message),publish(topic, message),send_to_channel(channel, message),send_to_spot(..., message)are not public contract members. A caller usessend(...).message(message).submit(). - A multipart payload accumulates via repeated
message(...)calls. Amessages(...)convenience method is allowed only when it delegates to the same builder contract and is declared on the public crate surface. - A Dealer socket does not expose protocol envelope helpers such as
request_frame(...)orreply(request_token, parts). Dealer can start a request withrequest(), but has no API-level peer routing id, so it cannot reply to an arbitrary token. - A message payload factory uses a fallible from-source contract:
Message::try_from(...)and aTryFromimplementation. A copy-only name such ascopy_fromis not part of the public contract. - Routing id construction uses the standard
Fromimplementations. Public helpers such asfrom_bytes,from_string,from_u32,from_uuid_bytesare not part of the public contract. Hex decoding may keepfrom_hex/try_from_hex. - No operation-start method family such as
send_no_wait,publish_with_flags,request_asyncis added. Keep one operation name, and let the builder absorb variants. The async surface also does not grow the operation start-point name — it is expressed as a builder terminator or aFuture-returning surface. - The async builder terminator is
submit()becauseasyncis a Rust keyword.
dealer.send().message(message).submit()?.admitted.await?;
let reply = dealer.request().message(request).submit()?.reply.await?;
HWM-managed send (PAIR send(), STREAM send(target), Received::send(), and
DEALER/ROUTER send) provides asynchronous submit() and synchronous submit_sync(). Request also
provides submit() and submit_sync() and keeps reply timeout on the builder. publish is not
in this classification: its terminal is synchronous
submit() -> Result<(), SubmitError> (lossy PUB semantics; under ZLINK_PUB_OPT_NODROP, a full
subscriber immediately returns Backpressured).
- A send Future progresses when the socket-local owner drains a Core DONTWAIT completion.
submit_sync() uses Core NONE admission. Future drop detaches only the waiter; a late completion
releases provisional-registry state and the native payload.
- PUB/XPUB publish and ROUTER reply retain separate synchronous operation contracts. The raw
ROUTER/Received reply terminal is the one-shot
ReplyOp<Ready>::submit() -> Result<(), SubmitError>. It submits a terminal reply or error reply
with one native call. A DEALER peer is subject to Application HWM (the queue byte threshold), PAUSED, and SNDTIMEO, so the
result can be BACKPRESSURED; a ROUTER peer uses the HWM-free Completion connection.
NOT_CONNECTED, TERMINATED, INVALID_ARGUMENT, and other submit failures return immediately as
Err(SubmitError).
Crate layout¶
The crate must expose clear public modules or re-exports.
- Core: context, options, version/capability lookup helpers, utility.
- Messaging: message, routing id, received metadata, topic message, subscription event, stream packet data.
- Sockets: pair, dealer, router, pub, sub, xpub, xsub, stream, typed options, request/reply, publish/subscribe, stream packet API.
- Eventing: monitor, monitor snapshot/event, poller, poll event, timer.
- Service: SPOT node, SPOT handle, topology snapshot, actor ref, actor lifecycle, operation builders.
- Error: the typed error/result domain that preserves core semantics.
The public crate may re-export frequently used types at the crate root, but keeps private FFI modules private.
Byte HWM and Auto-HWM¶
Core owns HWM calculation and queue admission.
CommonSocketOptions::set_send_high_water_mark(u64) and
set_receive_high_water_mark(u64) pass byte values losslessly to Core's exact
8-byte options. Their getters return Core's full uint64_t range as u64.
0 means unlimited.
Context options pass the byte-valued Core memory limit and budget, and the profile, unchanged to Core.
Planning, manual overrides, and admission follow Core HWM calculation and admission.
Context provides core_hwm_budget_snapshot() and
reset_core_hwm_budget_metrics(). The Rust binding supplies no runtime memory
hint. Input precedence is manual Core budget, explicit memory limit, then Core
fallback. If an explicit input exceeds a finite hard limit Core detected, the
binding preserves the existing configuration error corresponding to EINVAL
and does not clamp the value.
Core submission results map to Rust Result through the
common result projection.
SocketMonitorOpenOptions::monitor_hwm_bytes, passed through
SocketMonitor::open_with_options, is an exact u64 byte value. Zero selects
the Core monitor default; a positive value is forwarded unchanged, with no
message-count alias or conversion. Planned, applied, and
deferred HWM and in-flight usage in MonitorStatus are u64 bytes.
Pending-message counts remain display diagnostics; no slot, message-unit,
size-cap, or connection-bucket property is exposed. snd_pending_bytes and
rcv_pending_bytes are separate u64 byte values.
The Core budget snapshot projects ABI version/size, configured/runtime/resolved
memory limits, configured/effective budgets, planned/applied/manual-reserved
HWM, Core-queue/application/current/peak/provisional accounted bytes,
completion current/peak/pending and total-messaging values, monitor/instance
aggregates, application/completion queue counts,
outstanding_application_lease_count, retired_queue_count,
deferred_origin_credit_bytes, oversize/blocked/aggregate flags,
budget_generation, and measurement_epoch as u64/boolean values. Reset
preserves current, pending, and queue-count gauges, rebases both peaks to
current, clears epoch counters, and increments measurement_epoch.
application_accounted_bytes and the three owner-lifecycle fields are
ABI-reserved and always zero. An ABI version/size mismatch is an unsupported
error.
Receive flow state¶
The ReceiveFlowState enum provides Running = 0 and Paused = 1.
On the socket's common-option facade,
CommonSocketOptions::set_receive_flow_state(&self, value: ReceiveFlowState) -> Result<(), ConfigError>
returns Ok(()) on success or Err(ConfigError) on failure. ConfigResult is converted from
native errno: ENOTSUP becomes NotSupported, and EINVAL becomes InvalidArgument.
State, result, and monitor projection follow the common receive-flow contract.
Required capability coverage¶
Once the binding is aligned with the common .NET-baseline policy, the public crate must cover the following stable, user-facing capabilities.
- Context lifecycle, options, shutdown, auto-HWM recalculation, version, capability lookup, strerror.
- Message ownership, multipart payload, routing id, received metadata, topic message, subscription event, and stream packet values.
- Every socket family and its typed options.
SubSocket::subscription_at(index)andXSubSocket::subscription_at(index)return that index's subscription filter and whether it is a pattern. If that index doesn't exist, they returnNone. - Monitor, poller, timer, readiness semantics.
- SPOT node, SPOT handle, topology snapshot, actor, stream actor binding.
Rust names and the ownership model may differ from C, but behavior must match the meaning of the core capabilities.
Spot Get-Or-Create¶
Rust exposes SpotNode::get_or_create_spot(&RoutingId) -> Result<(Spot,
bool), ConfigError>. This maps directly to
zlink_spot_node_spot_get_or_new(...), and is not implemented by combining
spot_lookup and create_spot.
The returned Spot is owned by the caller and follows ordinary Drop
lifetime rules. The boolean is true only for the call that created the
logical spot.
Receive and Subscribe shape¶
- Data-plane receive and subscribe APIs use a reusable, caller-owned result storage.
ReceivedandTopicMessageown parts and metadata, with lifetime managed through reuse, consuming accessors, orDrop. Ordinary typed receive preserves ROUTER routing ID andReplyToken, and SUB topic/routing ID metadata. The common receive ownership contract defines the boundary with receive accounting.- Non-blocking no-data is distinguished from a hard receive failure.
- A SPOT readable dispatch event is a readiness notification. The caller drains the matching receive API until it returns no-data.
- Returned message data has clear ownership and lifetime. Borrowed data never outlives its native owner.
- A service control/admission receive path, such as Actor join request receive, may use
Option, a nullable equivalent, or a typed result-return form when that's clearer than the reusable data-plane storage. Even then, no-data and a hard receive failure are kept distinct.
Error and validation policy¶
- A native fixed-size id or string is validated before crossing the FFI boundary.
- Routing id, actor id, endpoint, channel name, and topic are never silently truncated.
- The submit, request, recv, handler, close, bind, connect, and config error domains are preserved.
- A public error must be checkable via a Rust type, not by parsing a string.
Performance policy¶
- The hot path does not use avoidable dynamic dispatch, avoidable allocation, avoidable byte copies, hidden sleeps, busy waits, broad locks, or thread joins.
- FFI bridge code must materialize public Rust values from the array filled by one Core whole-message receive call.
- It does not create a thread or timer per request when progress can be shared per handle.
- Perf, samples, and tests use only the public crate API.
Implementation checklist¶
- Public exports are intentional and documented.
- Raw FFI and unsafe state never leak through the public API.
- Resource ownership is enforced by Rust types and
Drop. - A trait is used only at a genuine abstraction point.
- A public free function and a builder convenience method are declared in a public crate module, not a runtime helper.
- Receive/subscription semantics match the common binding policy.
- Exceptions where service control/admission receive differs from the data plane's caller-provided storage are documented.
- Perf semantics match
bindings/c/perf. src/contractshas no import or export dependency onsrc/runtime.lib.rsimports a runtime module only to wire up constructors, and does not export a runtime module or a runtime implementation type name.- Tests, samples, and perf do not use private runtime imports.
- A native-backed resource is created via a crate root constructor or a contract method, and typed as a public contract type.
- The public surface has no compatibility-only alias, duplicate operation-start name, or deprecated wrapper.
Run the required verification commands from bindings/rust/.
- Run
cargo fmt --all --check. - Run
cargo test --workspace --all-targets. - Where clippy is available, run
cargo clippy --workspace --all-targets -- -D warnings. - Run
./tests/run_tests.sh. - Run
./samples/run_samples.shwhen a public example or a generation path changed. - Run
./perf/run_benchmarks.shand./perf/run_benchmarks_multi.shas a smoke gate when hot-path, receive, send, request, poller, timer, or service behavior changed. - Inspect rustdoc/public re-exports to confirm the crate export surfaces contract types, not runtime implementation modules.
- Search
src/contracts,tests,samples,perffor imports fromcrate::runtime,runtime::native, a raw FFI module, or a generated private file. Checksrc/lib.rsseparately to confirm a runtime import is used only to wire up constructors and never appears in a public signature.
Actor and Spot Route results¶
Rust exposes Actor and Spot route lookup results as public value types.
ActorRoutepreserves the resolved Actor ref, the Actor node RID, the current Spot RID, and the current Spot kind.SpotRoutepreserves the Spot RID, the owner node RID, and the Spot kind.SpotKinddistinguishes an Entry Spot from a user Spot. An invalid kind is not a successful route result.-
A SpotNode snapshot entry exposes the same Spot kind/current Spot fields as the core snapshot.
-
Rust exposes
SpotNode::send_to_actor(&ActorRef)andSpotNode::request_to_actor(&ActorRef), which take a resolved Actor ref. - The send operation, once submit succeeds, transfers ownership of one or more message parts, and completes once the Actor owner's mailbox takes them over.
- The request operation, once submit succeeds, transfers ownership of the request part and delivers the reply part the Actor handler produced.
- Rust must not resurrect the removed Discovery route table or resolver API as a compatibility helper.
Pull completion public contract¶
Rust package information follows its distribution metadata; the Core ABI version follows Core release metadata.
Rust provides submit_sync() returning a blocking Result and submit() returning a result object (Result<SendSubmission>/Result<RequestSubmission>: result and a boxed admitted future, plus a reply future for a request).
Completion-wait lifetime ends through Future drop or executor task abort.
Native completion IDs, user_context, and raw drain are not public APIs.
Submission results follow the common result projection;
completion joins, lifetime, and progress conditions for POLLCOMPLETION follow the
async execution model.
A ReplyToken carries both an Arc<RouterOwnerTag> created by the ROUTER wrapper and an opaque value.
Only pub(crate) fn from_native(owner, value) creates a token; equality, hashing, and reply-owner
validation use owner-tag identity and the value. StreamPacket is a reusable output, but close(self)
is a consuming terminal. A token provides no Default, raw numeric conversion, ordering,
serialization, or close operation. Concurrent recv into the same output is invalid-state. Message
references remain valid only until the next recv entry or output drop. Before the first bind/connect,
set_recv_mode accepts only Raw and Packet and rejects Unspecified.
Public interface¶
#[derive(Clone)]
pub struct ReplyToken {
owner: Arc<RouterOwnerTag>,
value: u64,
}
impl PartialEq for ReplyToken { /* Arc::ptr_eq(owner) + value */ }
impl Eq for ReplyToken {}
impl std::hash::Hash for ReplyToken { /* owner tag address + value */ }
impl std::fmt::Debug for ReplyToken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("ReplyToken")
}
}
// `impl Trait` fields are not allowed on stable Rust, so start with boxed futures.
pub struct SendSubmission {
pub result: SubmitResult, // OK | BACKPRESSURED, submit-time snapshot
pub admitted: Pin<Box<dyn Future<Output = Result<(), SubmitError>> + Send>>,
}
pub struct RequestSubmission {
pub result: SubmitResult,
pub admitted: Pin<Box<dyn Future<Output = Result<(), SubmitError>> + Send>>,
pub reply: Pin<Box<dyn Future<Output = Result<Vec<Message>, ZlinkError>> + Send>>,
}
impl SendOp<Ready> {
pub fn submit(self) -> Result<SendSubmission, SubmitError>;
pub fn submit_sync(self) -> Result<(), SubmitError>;
}
impl RequestOp<Ready> {
pub fn timeout(self, timeout: Duration) -> Self;
pub fn submit(self) -> Result<RequestSubmission, ZlinkError>;
pub fn submit_sync(self) -> Result<Vec<Message>, ZlinkError>;
}
impl Received {
pub fn reply_token(&self) -> Option<ReplyToken>;
}
impl ReplyOp<Ready> {
pub fn submit(self) -> Result<(), SubmitError>;
}
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StreamRecvMode {
Unspecified = 0,
Raw = 1,
Packet = 2,
}
pub struct StreamPacket {
routing_id: Option<RoutingId>,
header: Option<Message>,
body: Option<Message>,
}
impl Default for StreamPacket {
fn default() -> Self;
}
impl StreamPacket {
pub fn empty() -> Self;
pub fn is_empty(&self) -> bool;
pub fn routing_id(&self) -> Option<&RoutingId>;
pub fn header(&self) -> Option<&Message>;
pub fn body(&self) -> Option<&Message>;
pub fn close(self) -> Result<(), CloseError>;
}
impl StreamSocket {
pub fn recv_packet(
&self, out: &mut StreamPacket, flags: RecvFlags,
) -> Result<bool, RecvError>;
}
impl StreamSocketOptions<'_> {
pub fn recv_mode(&self) -> Result<StreamRecvMode, ConfigError>;
pub fn set_recv_mode(
&self, mode: StreamRecvMode,
) -> Result<(), ConfigError>;
}
The operation-start signatures are PAIR send(&self) -> SendOp<Empty>, DEALER
send(&self) -> SendOp<Empty> and request(&self) -> RequestOp<Empty>, ROUTER
send(&self, &RoutingId) -> SendOp<Empty>,
request(&self, &RoutingId) -> RequestOp<Empty>, and
reply(&self, &RoutingId, ReplyToken) -> ReplyOp<Empty>, and STREAM
send(&self, &RoutingId) -> SendOp<Empty>. A send factory captures the target in the builder.
Received::send() returns a SendOp<Empty> that captures the source target, and
Received::reply() returns a ReplyOp<Empty> that captures the source RID and token.
The public Rust surface contains no RoutedSendOp, send .timeout(Duration), RequestCallbackOp or
on_reply(), ReplyOp::flags(), STREAM callback, SocketMonitor::on_event(), ignore_handler(), or
snapshot(), Timer::on_fire(), or pair/generation member.
Monitor provides recv(), recv_with_flags(RecvFlags), status(), and close(&mut self). Monitor
DONTWAIT no-data is Ok(None). Timer provides start(&self, u64, u64), stop(&self), and
recv() -> Result<Option<u64>, RecvError>; Drop performs lifecycle cleanup. The native-header mirror
contains only ZLINK_OPT_PENDING_MAX_MSGS and ZLINK_OPT_PENDING_MAX_BYTES as pending options.
Monitor-event connection_id is used only for diagnostics and correlation, not as a send/reply target
or reconnect fence. Pending native options add no public high-level option method.
Implementation and contract-test verification requirements¶
Verify the following using only the public Rust interface, Result, Futures, and poller events. Each
item maps to one contract test.
Operations and completion
- Every socket's send factory returns
SendOp<Empty>, and send/request expose only the flag-free Future and synchronous terminals in the Public interface section. - Common completion, cancellation, and poller observations follow the execution-model verification requirements.
- When HWM/
PAUSEDwaiting expires for a raw reply submitted to a DEALER peer,Err(SubmitError)carries theBACKPRESSUREDcode; a reply submitted to a ROUTER peer retains the HWM-free result of the Completion connection.
ReplyToken and STREAM
- A token is
Clonebut notCopyorDefault, provides no raw numeric conversion, and itsDebugoutput does not expose the raw value. - Moving a ROUTER wrapper preserves owner-tag identity. After close and recreation, reply with a stale token fails before the native call.
recv_packet()fills the output after success and leaves it empty on no-data or error; consumingclose(self)andDropdo not release the payload twice.
Pull eventing
- Monitor and timer recv distinguish no-data as
Ok(None)and expose events and fire counts without callbacks.