Skip to main content

1. Start with Network Disabled

// ✅ Good: Start local-only, enable network on demand via discovery
let publisher = manager.register_publisher("topic", false, false)?;

// ⚠️ Only if you know remote subscribers exist
let publisher = manager.register_publisher("topic", true, false)?;
When you start with network disabled (enable_network_initial: false), TopicManager will automatically enable network transport when a remote subscriber is discovered. This saves resources when only local communication is needed.

2. Use Auto-Detection for Subscribers

// ✅ Good: Let TopicManager choose best transport
let subscriber = manager.register_subscriber("topic", None)?;

// ⚠️ Only if you need specific behavior
let subscriber = manager.register_subscriber("topic", Some(true))?;  // Force network
let subscriber = manager.register_subscriber("topic", Some(false))?; // Force local
Auto-detection (None) allows TopicManager to choose the optimal transport:
  • If a local publisher exists, it uses high-performance local IPC
  • If no local publisher exists, it falls back to network transport
  • If a local publisher appears later, it automatically switches to local mode

3. Keep Handles Alive

// ✅ Good: Manager and handles stay alive for the program's lifetime
let manager = TopicManager::create()?;
let pub1 = manager.register_publisher("topic1", false, false)?;
let pub2 = manager.register_publisher("topic2", false, false)?;
let sub = manager.register_subscriber("topic1", None)?;

// Keep all handles in scope
// ... use pub1, pub2, sub ...

// ❌ Bad: Manager dropped too early
{
    let manager = TopicManager::create()?;
    let pub = manager.register_publisher("topic", false, false)?;
} // Manager dropped here
// Publisher may not work correctly after manager is dropped

// ❌ Bad: Subscriber handle dropped
{
    let sub = manager.register_subscriber("topic", None)?;
} // Subscriber handle dropped here
// Subscription is automatically closed
Both the TopicManager and subscriber handles must remain alive for the lifetime you need them. Dropping the subscriber handle closes the subscription, and dropping the manager can cause issues with publishers and subscribers.

4. Use Typed Messages When Possible

// ✅ Good: Use send_message for type safety
struct SensorData {
    temperature: f32,
    timestamp: u64,
}

let data = SensorData { temperature: 23.5, timestamp: 1234567890 };
manager.send_message("sensors/temperature", &data)?;

// ⚠️ Use send only when necessary (raw bytes, pre-serialized data)
let raw_bytes = vec![0x01, 0x02, 0x03];
manager.send("raw_topic", raw_bytes)?;
send_message provides compile-time type checking and automatic schema tracking. Use send only when you have raw bytes or need to send data that doesn’t implement SerializableMessage.

5. Handle Subscriber Thread Safety

// ✅ Good: Clone Arc to share subscriber across threads
let subscriber = manager.register_subscriber("topic", None)?;

// In thread 1
let sub1 = Arc::clone(&subscriber);
thread::spawn(move || {
    let sub = sub1.lock().unwrap();
    // Use subscriber
});

// In thread 2
let sub2 = Arc::clone(&subscriber);
thread::spawn(move || {
    let sub = sub2.lock().unwrap();
    // Use subscriber
});
Since register_subscriber returns Arc<Mutex<Subscriber>>, you can safely share it across threads by cloning the Arc. Always lock the Mutex before accessing the subscriber.

6. Choose Appropriate Allocation Strategy

// ✅ Good: Use variable-size for large messages
let large_publisher = manager.register_publisher("large_data", false, true)?;

// ✅ Good: Use fixed-size for small, frequent messages (default)
let small_publisher = manager.register_publisher("small_data", false, false)?;
The is_variable_size parameter affects memory allocation:
  • false (fixed-size): Faster allocation, but limited message size. Use for small, frequent messages.
  • true (variable-size): Slower allocation, but supports larger messages. Use when messages may exceed the fixed buffer size.

Next Steps