ALVR-IPv6/CONTRIBUTING.md

5.2 KiB

Style

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

Naming

  • 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 an Option or Result. 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.

Top level definitions

  • 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.

Imports

  • 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.

Structs

  • 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.

Spacing

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.

Comments

  • 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.

Panicking and error handling

  • Use of panic!() is discouraged
  • When matching exhaustively, prefer unreachable!() over panic!() for certain branches.
  • unwrap() is discouraged, but prefer unwrap() over expect() (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 a unwrap() 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).

Code repetition and maintainability

  • 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 with Path vs string.

Structural soundness

  • Try to avoid invalid states in the data, using Rust enums. Example: do not use resumed + streaming boolean variables if the state resumed == false + streaming == true is invalid. Instead use enum State { Paused, Resumed, Streaming }.
  • Make full use of pattern matching with if let and while let, this reduces the use of unwrap().