Rust Standards & Tips
We use rust for a few projects right now, including both well supported Odyssey projects and lesser used experimental firmware projects. This page is split into 3 sections → general notes, linux/multithreaded notes, and firmware notes. Make sure to read the applicable sections before developing in rust at NER.
IDE Setup
Use rust-analyzer
extension and you should be all set for both embedded and applications context.
Helpful cargo commands
cargo run
to test your codecargo run --release
to test your code’s performancecargo clippy
to check your code passes lintercargo fmt
to format your codecargo test
to use your tests on the code
General Notes
Do not
use
(import) modules inlib.rs
!! This could global import things. Instead use full paths.Use
Result
andOption
. These structs are not just for libraries, use them for your own return results.Use
Result
for things that could return an error object.Use
Option
for things that could return null or may not “find” anything.
NEVER
unwrap
. Alternativesexpect
if an error is a critical failure and rareunwrap_or_default
if you do not care about the value and will no-op if the value is empty, 0, etc.match
the some/none or ok/err if you need to transform the error and/or break out/return from a functionunwrap_or_else
if you need to provide a default value and want to perform other operationsunwrap_or
to provide a const-context default
For functions with alot of parameters, use an options struct and pass that struct in.
For struct initialization, use a
new
function which returnsSelf
Printing
Debug print something (implement with
derive(Debug)
on the struct):{:?}
Display value of something (implement with
derive(Display)
on the struct):{}
If you use a library, do not use its dependencies directly. Instead add them to Cargo.toml.
Example: dont
use prisma_client_rust::chrono
, insteaduse chrono
as then if you removeprisma_client_rust
chrono will still work
Applications notes
Entrypoint/settings
Use
clap
andclap_derive
for all settings that should be changed at runtime. As rust is a compiled language, making a setting that may require changing involves rebuiliding and deploying a sometimes hefty application. Use clap_derive (as seen here) to expose your setting to the program. Use the environment variables as the example showsDockerization should use environment variables for all settings (see above)
Tokio/Threading
Use threads conservatively. If you need two+ things to happen on a loop, use a tokio::select in order to time multiple things that require access to the same variables.
Use print levels (trace, debug, info, warn) when you want a print to be included in the final product
Firmware notes
Use the embassy version of stuff. Embassy provides alternatives of normal functions for most stuff, use them. Do not consider adding a new library unless you are sure an embassy proejct doesn’t already include it.
Do all initialization of hardware using the
peripherals
variable inmain.rs
, and then once a new object is procured initialize that in the task.Ensure you arent copying things into functions. Make their context static or give a lifetime, do not copy a variable, especially a String.
Use DMA/await when possible. No matter how much work, avoid the
xxx_blocking
function available, use theawait
async functions. This allows better parallelism within code leading to optimal performance.