In this lesson, you will learn how to design error-handling strategies that minimize binary footprint and respect the tight stack constraints inherent in embedded systems.
When working on bare-metal hardware, the overhead of standard library abstractions like Box<dyn Error> is often unacceptable. These types perform heap allocations, which are frequently unavailable or dangerous in embedded environments. Instead, we must prioritize stack-allocated, enum-based error types.
Choosing Enumerations over Dynamic Dispatch
Dynamic dispatch adds a vtable pointer to your error types, increasing code size and introducing memory indirection. An enum is fixed-size and allows the compiler to perform exhaustive size checks.
// Preferred: Minimal size, stack-allocated, no heap.
#[derive(Debug)]
pub enum SensorError {
Timeout,
InvalidData(u8),
BusFault,
}
Avoiding "Stringy" Errors
Avoid using String or &str inside your error variants. Each string literal stored in an error variant ends up in the binary's .rodata section, consuming precious flash memory. If you must convey diagnostic information, use error codes or integer identifiers. If you absolutely need human-readable text, consider storing a &'static str identifier rather than a heap-allocated String.
Strict Stack Discipline
When propagating errors via ?, ensure your error types are small. A large enum with many variants, each holding a different data payload, can result in the largest variant determining the size of the entire type. This causes the stack to grow unnecessarily every time a function returns an Result. Use Result<(), SensorError> instead of Result<LargeStruct, SensorError> where possible, and avoid nesting errors if they result in large, complex types on the stack.
Inlining for Size
For tiny, performance-critical error checks, using #[inline] on functions that return a Result can help the compiler prune branches and eliminate the need for the error-handling machinery to exist as a separate function call in the final instruction stream.
Key Takeaway: Use unit-sized enums or small, stack-allocated structures to represent errors, avoiding heap allocation and large data payloads to keep your binary small and your stack predictable.