Checklist for code style. This is on top of common Rust styling rules. These rules are not mandatory but I might point them out if not respected in PRs :) -zarik
-
Respect Rust naming conventions (not respecting this will cause a warning).
-
Add useful information in the name, with the exception of indices for iterating.
-
Do not put type or scope information in the name.
-
Avoid abbreviations.
-
Avoid prefixes and suffixes.
-
if necessary prefer suffixes rather than prefixes.
-
_ref
or_mut
suffixes are accepted when you want to put emphasis on that the variable is a mutable reference, and so assigning values has side effects, even if not consumed later. Suffixes are not needed if the variable is only mutable or instead a immutable reference. -
Use
maybe_
prefix if the variable is anOption
orResult
. Omit if it's clear from the context. Never use it for parameter or field definitions. -
Shadowing is encouraged.
-
If shadowing cannot be used and two variables have similar meaning but different types, suffix the variable with the least useful or least specialized type with its type. Example:
let myfile_string = "./file.txt"; let myfile = Path::new(my_file_string);
-
If both directory and file paths are used in the same context, suffix directories with
_dir
and files with_path
. Suffix file names with_fname
.
- For each file, define in order: private imports, public imports, ffi bindings import, private constants, public constants, private structs, public structs, private top level functions, public top level functions.
- Do not leave spaces between imports, only between the private and public import blocks.
- Define imports in alphabetical order (use cargo fmt).
- Group imports using braces when there are common parts of the path.
- Prefer adding trait bounds to the impl type parameters instead of struct type parameters
- Define in order: struct, Default impl, custom impl, Drop impl, all in the same module. Do not split the custom impl.
Smartly use empty newlines between blocks of code to improve legibility
- Rust recommends not to specify the return keyword when returning at the end of a function. To put emphasis on the return expression, isolate it with a empty new line just before.
- Inside each block (function, if/else/while/for etc or just braces) make so that there are roughly 2 to 6 blocks of code separated by empty lines for the amount of code that fits in a single screen (long functions that span multiple screens can have way more than 6 block). Check existing codebase for an example.
- Spaces between groups of statements should be done so the groups are similar is size and that each achieves a specific purpose. You should be able to easily describe what the group does in few words, even if you don't comment it (because the meaning should be self evident).
- Do not define variables at the start of the function/block, but do define them at the start of the functional group delimited by spaces.
- It's important to use comments when the meaning or inner workings of a piece of code is not clear from the context. Well-named symbols (variables and functions) are often enough. In doubt do use comments.
- Do not add comments about how certain parts of the language/std library work, unless it's about quirks of features.
- Use of
panic!()
is discouraged - When matching exhaustively, prefer
unreachable!()
overpanic!()
for certain branches. unwrap()
is discouraged, but preferunwrap()
overexpect()
(bubble up the error instead).- Prefer
.get()
to index a collection rather than[]
, unless extremely certain it will never index out of bounds. - Add a
// # Safety
comment before a statement that contains aunwrap()
or raw indexing, explaining why it should never crash. - Use
todo!()
to mark unfinished code (it returns!
and so it helps with the missing return statement).
- Lean towards the DRY rule, without overdoing it.
- Extract a piece of code (in a function or lambda) only when it is used two or more times and it doesn't depend on many parameters.
- Always extract constants for "arbitrary" values (literals) which are chosen with no absolute rule and makes sense to change in the future. Example: time interval between resending discovery packet. Opposite example: number of eyes on a human head (it's always going to be 2, no need to use a constant to change its value in the future :) ).
- Prefer defining constants at the start of the file, even if used locally in a single function.
- Prefer using "complex" types for constants, if the std library allows it. Example: prefer using
Duration
instead of an integer type if the constant represents a time duration. Same withPath
vs string.
- Try to avoid invalid states in the data, using Rust enums. Example: do not use
resumed
+streaming
boolean variables if the stateresumed == false
+streaming == true
is invalid. Instead useenum State { Paused, Resumed, Streaming }
. - Make full use of pattern matching with
if let
andwhile let
, this reduces the use ofunwrap()
.