56 lines
		
	
	
		
			1.9 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			56 lines
		
	
	
		
			1.9 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| = 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 resources 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 safety 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
 | |
|     ()
 | |
| }
 | |
| ----
 |