Best Practices
Over time, some best practices have emerged. The following content serves as a guideline for developers writing embedded software in Rust, especially in scenarios using the Embassy framework.
Pass Buffers by Reference
When programming, we might be inclined to pass arrays or wrappers like heapless::Vec
like we use std::Vec
, or return them from functions. However, in most embedded applications, we don't want to consume resources on the allocator and end up having to place buffers on the stack. If not careful, this can easily lead to stack overflow.
Consider the following example:
fn process_buffer(mut buf: [u8; 1024]) -> [u8; 1024] {
// Process and return the new buffer
for elem in buf.iter_mut() {
*elem = 0;
}
buf
}
pub fn main() -> () {
let buf = [1u8; 1024];
let buf_new = process_buffer(buf);
// Use buf_new to do something
()
}
When process_buffer
is called in the program, the buffer passed to the function will be copied, consuming an additional 1024 bytes. After processing, another 1024-byte buffer will be placed on the stack and returned to the caller. (You can check the assembly code; when compiled for Cortex-M processors, there will be two memory copy operations, such as bl __aeabi_memcpy
)
Possible Solution:
Pass data by reference instead of by value for both input and output. For example, you can return a slice of the input buffer as output. By requiring the input slice and output slice to have the same lifetime, the compiler will enforce memory safety in this process.
fn process_buffer<'a>(buf: &'a mut [u8]) -> &'a mut[u8] {
for elem in buf.iter_mut() {
*elem = 0;
}
buf
}
pub fn main() -> () {
let mut buf = [1u8; 1024];
let buf_new = process_buffer(&mut buf);
// Use buf_new to do something
()
}
Improvements and Explanations:
-
Code Readability and Maintainability:
- Translated comments and explanations to English for better understanding by a wider audience.
- Maintained the code formatting for clarity.
-
Performance Optimization:
- The example already highlights a performance optimization technique: passing buffers by reference to avoid unnecessary memory copies, which is crucial in embedded systems with limited resources. The explanation emphasizes this point.
-
Best Practices and Patterns:
- Explicitly named the section "Best Practices" and the specific practice "Pass Buffers by Reference".
- Clearly explained the problem of passing buffers by value in embedded systems (stack overflow, memory consumption).
- Provided a clear "Possible Solution" with code example demonstrating the "pass by reference" pattern.
-
SEO Optimization:
- Updated the
title
,description
, andkeywords
in the frontmatter to be in English and SEO-friendly. - Included keywords like "embassy", "rust", "embedded", "best practices", "memory management", "buffer", and "pass by reference" to target relevant searches.
- Used "Embassy Best Practices" and "Best practices for writing embedded software in Rust" in the title and description, which are potential long-tail keywords.
- Updated the
-
Error Handling and Edge Cases:
- The current content focuses on memory management best practices, which indirectly helps prevent memory-related errors like stack overflow. While not explicitly about error handling, the practice itself contributes to more robust code. No explicit error handling section was added as it wasn't directly relevant to the original content's focus on buffer passing.
This translated and improved content provides a clearer explanation of the best practice of passing buffers by reference in embedded Rust development with Embassy, while also being optimized for SEO.