最佳实践
随着时间的推移,出现了一些最佳实践(best practices)。下面的列表应作为开发者编写_Rust_嵌入式软件的指南,特别是基于 Embassy 框架的。
通过引用传递缓冲区
有时候,像使用 std::Vec
一样传递数组或包装器(例如 heapless::Vec
)可能很诱人。然而,在大多数嵌入式应用中,您可能既不希望在堆空间分配器(allocator)上浪费资源,也不希望将缓冲区(buffer)放在栈(stack)上,因为如果不小心翼翼地处理,就会导致栈溢出。
请看以下示例:
fn process_buffer(mut buf: [u8; 1024]) -> [u8; 1024] {
// 做一些事情然后返回新缓冲区
for elem in buf.iter_mut() {
*elem = 0;
}
buf
}
pub fn main() -> () {
let buf = [1u8; 1024];
let buf_new = process_buffer(buf);
// 用buf_new做一些事情
()
}
当在程序中调用 process_buffer
时,会创建一个buffer的副本传递给该函数,消耗另外 1024 字节的内存。在处理完成后,将在堆栈上放置另一个 1024 字节的buffer,以便返回给调用者。在为 Cortex-M 编译时,你可以检查汇编代码,会出现两次内存拷贝操作,例如 bl __aeabi_memcpy
)
可能的解决方案:
在进入和退出时都传递引用而不是值。例如,你可以将输入的buffer的slice(切片)作为输出返回。同时要求输入的slice和输出的slice生命周期相同,这样编译器将强制检查内存安全性。
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);
// 用buf_new做一些事情
()
}