Rust Best Practices
Evolving FP Complete recommendations document. Can include anything from recommended libraries and tools to how to use language features. If you think it's a good idea, add it! We can worry about organizing it later.
- Don't panic! Use errors
- Be liberal in what you accept
- Avoid references in structs
- Use an Rc or Arc when needed
- CLI: use clap
- Lock your repos
- Test your code
- Lint your code
- IDE
- Logging
- Public vs private fields
Don't panic! Use errors
- Custom app error type
- Use
?a lot - Learn to use
ok_errandmap_err - Crates helping with error handling
- Provide a user friendly Display for your errors
Context for errors
- Don't simply package up an
std::io::Errorand call it a day - Provide some additional information like "Was trying to open file XXX and received this error".
- Much more user friendly, much easier to debug
Be liberal in what you accept
- Prefer taking
&stroverStringin function arguments - Similarly, take
&[T]instead ofVec<T>. If you just need to iterate over something, consider accepting a more generalIntoIterator<Item = T>. - Avoids forcing someone to create an owned copy in many cases
Avoid references in structs
- References in
structs make you include lifetime parameters - Generally: avoid lifetime parameters if you can, simplifies code a lot
- Usually, you'll want a
String, not a&'a str, in yourstructs
Use an Rc or Arc when needed
- Many borrow checker issues can be short-circuited by throwing
RcorArcinto things - Don't worry too much about optimizing these cases
- Also, if it's not too expensive, consider a
.clone()as well - A good example of not doing this and wasting a lot of time: https://www.fpcomplete.com/blog/avoiding-duplicating-strings-rust/
CLI: use clap
clapis a really nice library- Forces you to deal with "impossible" error cases
- Less error handling in your own code
- amber has a nice example of it
Lock your repos
- Include
Cargo.lockand arust-toolchain - Makes it more reproducible
- If you use Git deps, do it by commit SHA
Test your code
- Obviously
- Consider using quickcheck, it's nice!
- For most unit tests, include within a
#[cfg(test)] mod tests { ... }section. - For integration tests and larger unit tests, place in a separate
.rsin thetestsdirectory.
Lint your code
cargo clippyandcargo fmtare good- Include them in CI
- Github Actions has nice stuff for that already
IDE
- Use Rust Analyzer as the language server.
- Visual Studio Code: the extensions
rust-lang.rustandmatklad.rust-analyzerare popular.
Logging
- Use the
tracingcrate for logging in all libraries and applications.
Public vs private fields
From a discussion in an engineering meeting on June 11, 2025.
When declaring fields on a struct in Rust, you need to determine the visibility: public, private, or something in between (like pub(crate) or pub(in crate::mod_tree)). This applies to functions, methods, enums, and more as well, but the question comes up most often for structs: should the fields be public or private?
There will always be some room for variation and disagreement about this, and it's almost always a case-by-case basis. However, the following general guidelines work as good defaults.
- There's a big difference between public, published libraries and libraries that will be used internally by our team and/or customers. The guidelines here apply to the internal variety. We're punting on published library guidelines for now.
- If there are invariants to be maintained within a
struct, such as ensuring that a field can never be 0, keep the field private. This is good encapsulation behavior. - If the
structis mostly simple data (i.e., the Rust equivalent of a Plain Old Java Object), defaulting to public fields makes sense. - More complex fields, such as
Mutexs, should probably default to being more private. - In general, using private fields can be a good way to protect consumers of a library from API breakage when internals change. However, when we're working in an internal capacity, it doesn't much matter if we break things in this way. Usually, the library author and application author are the same person, and regardless we're all on the same team anyway.