54 lines
1.9 KiB
Text
54 lines
1.9 KiB
Text
|
= Best Practices
|
||
|
|
||
|
Over time, a couple of best practices have emerged. The following list should serve as a guideline for developers writing embedded software in _Rust_, especially in the context of the _Embassy_ framework.
|
||
|
|
||
|
== Passing Buffers by Reference
|
||
|
It may be tempting to pass arrays or wrappers, like link:https://docs.rs/heapless/latest/heapless/[`heapless::Vec`], to a function or return one just like you would with a `std::Vec`. However, in most embedded applications you don't want to spend ressources on an allocator and end up placing buffers on the stack.
|
||
|
This, however, can easily blow up your stack if you are not careful.
|
||
|
|
||
|
Consider the following example:
|
||
|
[,rust]
|
||
|
----
|
||
|
fn process_buffer(mut buf: [u8; 1024]) -> [u8; 1024] {
|
||
|
// do stuff and return new buffer
|
||
|
for elem in buf.iter_mut() {
|
||
|
*elem = 0;
|
||
|
}
|
||
|
buf
|
||
|
}
|
||
|
|
||
|
pub fn main() -> () {
|
||
|
let buf = [1u8; 1024];
|
||
|
let buf_new = process_buffer(buf);
|
||
|
// do stuff with buf_new
|
||
|
()
|
||
|
}
|
||
|
----
|
||
|
When calling `process_buffer` in your program, a copy of the buffer you pass to the function will be created,
|
||
|
consuming another 1024 bytes.
|
||
|
After the processing, another 1024 byte buffer will be placed on the stack to be returned to the caller.
|
||
|
(You can check the assembly, there will be two memcopy operations, e.g., `bl __aeabi_memcpy` when compiling for a Cortex-M processor.)
|
||
|
|
||
|
*Possible Solution:*
|
||
|
|
||
|
Pass the data by reference and not by value on both, the way in and the way out.
|
||
|
For example, you could return a slice of the input buffer as the output.
|
||
|
Requiring the lifetime of the input slice and the output slice to be the same, the memory safetly of this procedure will be enforced by the compiler.
|
||
|
|
||
|
[,rust]
|
||
|
----
|
||
|
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);
|
||
|
// do stuff with buf_new
|
||
|
()
|
||
|
}
|
||
|
----
|