pub trait ClientTrait {
type Error;
// Required methods
fn send_request_raw(&mut self, request: &[u8]) -> Result<i64, Self::Error>;
fn take_response_raw(
&mut self,
reply_buf: &mut [u8],
) -> Result<Option<(usize, i64)>, Self::Error>;
// Provided methods
fn send_request<S>(
&mut self,
request: &<S as RosService>::Request,
req_buf: &mut [u8],
) -> Result<(), Self::Error>
where S: RosService,
Self::Error: From<TransportError> { ... }
fn take_response<S>(
&mut self,
reply_buf: &mut [u8],
) -> Result<Option<<S as RosService>::Reply>, Self::Error>
where S: RosService,
Self::Error: From<TransportError> { ... }
fn register_waker(&self, _waker: &Waker) { ... }
fn start_server_discovery(
&mut self,
_timeout_ms: u32,
) -> Result<(), Self::Error> { ... }
fn poll_server_discovery(&mut self) -> Result<Option<bool>, Self::Error> { ... }
fn service_is_ready(&self) -> Result<bool, Self::Error>
where Self::Error: From<TransportError> { ... }
}Expand description
Service client trait for sending requests.
§Threading
&mut self on every method — the client is single-owner. For
fan-out request patterns, create one client per worker thread.
§Calling pattern
All in-tree backends route blocking waits through the executor:
send_request_raw(buf)— non-blocking; returns once the request is queued for transmission.- The executor’s
drive_ioruns. take_response_raw(buf)— non-blocking; returnsOk(Some(len))when the reply is back.
Phase-301 (issue 0240): the deprecated blocking call_raw path is
DELETED — send_request_raw + take_response_raw is the one
request/reply path, and both are required for service-capable
backends.
Required Associated Types§
Required Methods§
Sourcefn send_request_raw(&mut self, request: &[u8]) -> Result<i64, Self::Error>
fn send_request_raw(&mut self, request: &[u8]) -> Result<i64, Self::Error>
Send a service request without waiting for a reply (non-blocking).
Returns the SEQUENCE ID the backend assigned. The caller must
subsequently poll take_response_raw and
match that id against the one the reply carries.
Issue 0778 — this returned () until 2026-08-25, and every backend
computed an id and discarded it. With nothing to correlate by, a client
with two calls outstanding could not tell the replies apart, so each
backend picked a policy: cyclonedds abandoned the older request, zenoh
took the first reply. Both are wrong for send_goal and
SetParameters, which travel this path and are not idempotent.
Sourcefn take_response_raw(
&mut self,
reply_buf: &mut [u8],
) -> Result<Option<(usize, i64)>, Self::Error>
fn take_response_raw( &mut self, reply_buf: &mut [u8], ) -> Result<Option<(usize, i64)>, Self::Error>
Poll for a reply (non-blocking).
Returns Ok(Some((len, sequence_id))) when a reply has arrived,
Ok(None) if not yet available, or Err on failure. The
sequence_id is the one send_request_raw
returned for the request this answers.
It used to say “a reply to the MOST RECENTLY sent request”, which was the single-outstanding-call assumption written into the contract.
Provided Methods§
Sourcefn send_request<S>(
&mut self,
request: &<S as RosService>::Request,
req_buf: &mut [u8],
) -> Result<(), Self::Error>
fn send_request<S>( &mut self, request: &<S as RosService>::Request, req_buf: &mut [u8], ) -> Result<(), Self::Error>
Send a typed service request without waiting for a reply (non-blocking).
Serializes the request into req_buf and calls send_request_raw.
Sourcefn take_response<S>(
&mut self,
reply_buf: &mut [u8],
) -> Result<Option<<S as RosService>::Reply>, Self::Error>
fn take_response<S>( &mut self, reply_buf: &mut [u8], ) -> Result<Option<<S as RosService>::Reply>, Self::Error>
Poll for a typed reply to the most recently sent request (non-blocking).
Calls take_response_raw and deserializes if available.
Sourcefn register_waker(&self, _waker: &Waker)
fn register_waker(&self, _waker: &Waker)
Register an async waker to be notified when a reply arrives.
Called from Future::poll() implementations to store the waker.
The transport backend calls waker.wake() from its reply callback
when a response is available, enabling event-driven async without
busy-polling.
Default: no-op (backends that don’t support waking simply ignore this).
Sourcefn start_server_discovery(
&mut self,
_timeout_ms: u32,
) -> Result<(), Self::Error>
fn start_server_discovery( &mut self, _timeout_ms: u32, ) -> Result<(), Self::Error>
Begin a server-discovery query on this client (non-blocking).
Models rclcpp::ClientBase::wait_for_service machinery: the backend
fires off a discovery probe (typically a Zenoh liveliness query
against the matching server’s wildcarded liveliness keyexpr) and
the caller polls poll_server_discovery
to collect the result.
Default impl: no-op success. Backends without a discovery channel
(or those that always assume the server is reachable) can leave
this default and have poll_server_discovery return
Ok(Some(true)) immediately.
Sourcefn poll_server_discovery(&mut self) -> Result<Option<bool>, Self::Error>
fn poll_server_discovery(&mut self) -> Result<Option<bool>, Self::Error>
Poll an in-flight server-discovery query.
Ok(Some(true))— at least one matching server has reported back; safe to send the first request.Ok(Some(false))— discovery query finished without finding any matching server (timeout / no-replies).Ok(None)— query still in flight.Err(_)— transport-level failure unrelated to server presence.
Default impl: returns Ok(Some(true)) (i.e., “server is always
assumed reachable”). The Zenoh backend overrides this with a
liveliness-token check.
Sourcefn service_is_ready(&self) -> Result<bool, Self::Error>
fn service_is_ready(&self) -> Result<bool, Self::Error>
Whether a matching service server is currently discoverable.
Mirrors rclcpp::ClientBase::service_is_ready — the NAME is upstream’s.
The SHAPE is rcl’s: rcl_service_server_is_available(node, client, bool *is_available) returns RCL_RET_OK “if the check was made
successfully (regardless of the service readiness)”, i.e. the return
code says whether the CHECK worked and the out-param carries the ANSWER.
rclcpp collapses that to a bare bool and moves the error to
exceptions; RFC-0018 forbids exceptions, so Result<bool, _> is how the
same contract is expressed here (phase-379 W6, RFC-0036).
Returns Ok(true) if at least one matching server has been
discovered, Ok(false) if none yet, or Err(_) if the
backend cannot answer (e.g. XRCE — micro-XRCE-DDS-Client has
no participant enumeration). Distinct from
is_server_ready, which collapses
“don’t know” and “no server” into the same false answer.
User-facing surface: Client<S>::server_available() in Rust,
nros_client_server_available() in C/C++. Clients use this
to gate the first request so a startup-ordering race
(client opens before server’s discovery announcement lands)
doesn’t surface as a request-side timeout.
Default impl: Err(TransportError::Unsupported) — backends
that support graph introspection (zenoh queryable interest,
DDS built-in topic readers) opt in by overriding.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".