Rust Examples
Working source for each example lives under
examples/rust/ in the AstuteDDS source tree.
Hello World
A two-binary crate that publishes and consumes UTF-8 strings on
HelloWorldTopic.
examples/rust/hello_world/src/hello_publisher.rsexamples/rust/hello_world/src/hello_subscriber.rs
See Quick Start for the full source.
Sensor (multi-field CDR)
TemperatureSensor carries a 24-byte XCDR1 little-endian payload:
| Offset | Field | Type |
|---|---|---|
| 0 | CDR header | 4 B (00 01 00 00) |
| 4 | sensor_id |
u32 |
| 8 | temperature_c |
f32 |
| 12 | value_sine |
f32 |
| 16 | value_triangle |
f32 |
| 20 | sequence |
u32 |
Encode
fn encode_sensor_reading(
sensor_id: u32,
temperature_c: f32,
value_sine: f32,
value_triangle: f32,
sequence: u32,
) -> [u8; 24] {
let mut buf = [0u8; 24];
buf[0..4].copy_from_slice(&[0x00, 0x01, 0x00, 0x00]); // XCDR1 LE header
buf[4..8].copy_from_slice(&sensor_id.to_le_bytes());
buf[8..12].copy_from_slice(&temperature_c.to_le_bytes());
buf[12..16].copy_from_slice(&value_sine.to_le_bytes());
buf[16..20].copy_from_slice(&value_triangle.to_le_bytes());
buf[20..24].copy_from_slice(&sequence.to_le_bytes());
buf
}
Decode
struct SensorReading {
sensor_id: u32,
temperature_c: f32,
value_sine: f32,
value_triangle: f32,
sequence: u32,
}
fn decode_sensor_reading(raw: &[u8]) -> Option<SensorReading> {
if raw.len() < 24 { return None; }
Some(SensorReading {
sensor_id: u32::from_le_bytes(raw[4..8].try_into().ok()?),
temperature_c: f32::from_le_bytes(raw[8..12].try_into().ok()?),
value_sine: f32::from_le_bytes(raw[12..16].try_into().ok()?),
value_triangle: f32::from_le_bytes(raw[16..20].try_into().ok()?),
sequence: u32::from_le_bytes(raw[20..24].try_into().ok()?),
})
}
Poll loop with timeout
use astutedds::{DataReaderQos, DomainParticipant, Error, HistoryKind, Reliability};
use std::time::{Duration, Instant};
fn main() -> astutedds::Result<()> {
let dp = DomainParticipant::new(0)?;
let topic = dp.create_topic("TemperatureSensor", "SensorReading")?;
let sub = dp.create_subscriber()?;
let reader = sub.create_datareader(&topic, DataReaderQos {
reliability: Reliability::ASTUTEDDS_RELIABLE,
history_kind: HistoryKind::ASTUTEDDS_KEEP_LAST,
history_depth: 10,
..Default::default()
})?;
const SILENCE: Duration = Duration::from_secs(60);
let mut last = Instant::now();
loop {
match reader.take_next() {
Ok(bytes) => {
last = Instant::now();
if let Some(s) = decode_sensor_reading(&bytes) {
println!("seq={:>5} temp={:.2} °C", s.sequence, s.temperature_c);
}
}
Err(Error::NoData) => {
if last.elapsed() > SILENCE {
println!("(silent for 60 s — exiting)");
break;
}
std::thread::sleep(Duration::from_millis(100));
}
Err(e) => return Err(e),
}
}
Ok(())
}
QoS overrides
DataWriterQos and DataReaderQos are Copy structs — clone-and-override
with struct-update syntax:
use astutedds::{DataWriterQos, Durability, HistoryKind, Reliability};
let qos = DataWriterQos {
reliability: Reliability::ASTUTEDDS_RELIABLE,
history_kind: HistoryKind::ASTUTEDDS_KEEP_LAST,
history_depth: 100,
durability: Durability::ASTUTEDDS_TRANSIENT_LOCAL,
};
Sharing entities across threads
All wrapper types are Send + Sync, so you can share them with Arc:
use std::sync::Arc;
use std::thread;
let dp = Arc::new(DomainParticipant::new(0)?);
let topic = dp.create_topic("Heartbeat", "Heartbeat")?;
let pub_ = dp.create_publisher()?;
let writer = Arc::new(pub_.create_datawriter(&topic, Default::default())?);
let w = writer.clone();
thread::spawn(move || {
for i in 0..100 {
let _ = w.write(format!("beat {i}").as_bytes());
}
});
Note
Lifetimes still apply — the Topic, Publisher, and DataWriter
must not outlive the DomainParticipant. When sharing across
threads, keep the participant alive in the outermost scope (or via
its own Arc).
Multi-domain isolation
let dp_lab = DomainParticipant::new(0)?;
let dp_sim = DomainParticipant::new(42)?;
Participants in different domain_ids exchange no traffic — useful for
isolating staging, integration, and production traffic on the same host.