Skip to main content

ClientTrait

Trait ClientTrait 

Source
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: RosService>(
        &mut self,
        request: &S::Request,
        req_buf: &mut [u8],
    ) -> Result<(), Self::Error>
       where Self::Error: From<TransportError> { ... }
    fn take_response<S: RosService>(
        &mut self,
        reply_buf: &mut [u8],
    ) -> Result<Option<S::Reply>, Self::Error>
       where 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:

  1. send_request_raw(buf) — non-blocking; returns once the request is queued for transmission.
  2. The executor’s drive_io runs.
  3. take_response_raw(buf) — non-blocking; returns Ok(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§

Source

type Error

Error type for service operations

Required Methods§

Source

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.

Source

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§

Source

fn send_request<S: RosService>( &mut self, request: &S::Request, req_buf: &mut [u8], ) -> Result<(), Self::Error>
where Self::Error: From<TransportError>,

Send a typed service request without waiting for a reply (non-blocking).

Serializes the request into req_buf and calls send_request_raw.

Source

fn take_response<S: RosService>( &mut self, reply_buf: &mut [u8], ) -> Result<Option<S::Reply>, Self::Error>
where Self::Error: From<TransportError>,

Poll for a typed reply to the most recently sent request (non-blocking).

Calls take_response_raw and deserializes if available.

Source

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).

Source

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.

Source

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.

Source

fn service_is_ready(&self) -> Result<bool, Self::Error>
where Self::Error: From<TransportError>,

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".

Implementors§