Skip to main content
Your middleware shouldn’t be a source of production crashes. A robot middleware is the runtime that all other code depends on. When it segfaults, everything stops. When it has undefined behavior, failures are intermittent and unreproducible. When it has data races, your system works in testing and fails in deployment. ROS2’s C++ backend is exposed to all three. Cerulion’s Rust backend eliminates them at compile time.

Three Safety Dimensions

Memory safety — no segfaults

Rust’s ownership system guarantees that memory is always valid when accessed. No use-after-free, no double-free, no null pointer dereferences. The compiler enforces these rules — they can’t be circumvented by mistake.Why this matters for middleware: A segfault in your camera node crashes your camera node. A segfault in the middleware crashes every node on the machine. The middleware is the last place you want memory safety bugs.
// Rust prevents use-after-free at compile time
let publisher = create_publisher("image");
let message = Image::new();
publisher.send(message);
// message is moved — can't accidentally use stale data
// Compiler error if you try to access message after send
In C++, the equivalent code compiles and runs — until it doesn’t. The message object might be valid, or it might be freed. You won’t know until production.
Undefined behavior in C++ means the compiler can do anything — optimize away safety checks, reorder operations, or produce code that works on your machine and fails on your customer’s. ROS2’s C++ codebase can’t prove the absence of UB.Why this matters for middleware: UB makes failures non-reproducible. A bug that manifests on one platform, one compiler version, or one optimization level but not another is nearly impossible to diagnose. When your middleware has UB, you can’t trust any of its guarantees.Rust’s type system and borrow checker make UB impossible in safe code. The only way to introduce UB is through explicitly marked unsafe blocks, which are minimal and auditable in Cerulion’s codebase.
Rust’s type system prevents data races at compile time. If two threads can access the same data, the compiler requires explicit synchronization. You can’t accidentally share mutable state between the publisher thread and the network background thread.Why this matters for middleware: Cerulion’s dual-transport architecture has inherent concurrency — local publishing, network background threads, subscriber callbacks, and the scheduler all run simultaneously. In C++, each interaction point is a potential data race. In Rust, the compiler verifies correctness.
// Rust enforces thread safety at compile time
// This publisher can safely be used from the scheduler thread
// while the network thread handles serialization
let publisher = topic_manager.create_publisher::<Image>("camera/frame");

// The compiler guarantees no data races between:
// - The scheduler calling publisher.send()
// - The background thread serializing for network
// - The subscriber reading from shared memory

Not a Rust Advocacy Argument

This isn’t about Rust being a better language in general. It’s about what properties matter for a specific category of software: a runtime that other code depends on.
Cerulion provides C++ and Python APIs — you write nodes in whatever language fits your team. Only the middleware runtime itself is Rust. Your application code, your algorithms, your business logic can be in any supported language.Coming Soon Python SDK and C++ SDK are in development.
The argument is narrow: the middleware layer specifically benefits from compile-time safety guarantees because:
  • It runs continuously in production
  • All other software on the robot depends on it
  • Failures are catastrophic (everything stops) rather than isolated
  • Concurrency is inherent to its architecture, not optional

Next Steps

Determinism

How the runtime achieves deterministic execution

Components

TopicManager, Publisher, and Subscriber internals