Skip to main content

Rust

We use rustfmt to format our code. This tool is part of the Rust toolchain and will format your code according to the Rust style guide. We also use clippy to catch common mistakes and improve the quality of our code.

This document covers some additional guidelines that are not enforced by rustfmt or clippy. And will generally allow you to write more idiomatic Rust code.

Avoid panics​

Panics are highly discouraged in the Bitwarden codebase outside of tests. Errors should be handled gracefully and returned to the caller. clippy will forbid you from using unwrap. While expect is allowed, it should be used sparingly and should always provide a helpful message indicating why it can never occur.

Some scenarios where expect are allowed are:

  • Calling libraries that guarantee that the allowed inputs never results in Err or None.
  • Operating on slices or arrays where the index is guaranteed to be within bounds.

Pattern matching​

Rust provides a powerful pattern matching system that can be used for a variety of tasks. However, care should be taken to not rely overly on match statements. In many cases there are more concise and readable alternatives especially when working with Option and Result.

if let​

Use if let when you only care about one arm of a match.

// Bad
match result {
Ok(value) => outer.append(value),
_ => (),
}

// Good
if let Ok(value) = result {
outer.append(value);
}

Options​

We use clippy to enforce general guidelines for working with Option. Generally reach for methods like:

map​

Use map when you want to transform the value inside the Option.

// Bad
match Some(0) {
Some(x) => Some(x + 1),
None => None,
};

// Good
Some(0).map(|x| x + 1);

and_then​

Use and_then when you want to chain multiple operations that return an Option.

func(x: i32) -> Option<i32>;

// Bad
match Some(0) {
Some(x) => func(x + 1),
None => None,
};

// Good
Some(0).and_then(|x| func(x + 1));

unwrap_or​

Use unwrap_or when you want to provide a default value.

// Bad
match Some(0) {
Some(x) => x,
None => 1,
};

// Good
Some(0).unwrap_or(1);

ok_or​

Use ok_or when you want to convert an Option to a Result.

// Bad
match Some(0) {
Some(x) => x,
None => Err("error"),
};

// Good
foo.ok_or("error");

Results​

When working with Result it might be tempting to use match to get the value out of the result. Instead, use the ? operator to propagate the error.

map_err​

Use map_err when you want to transform the error inside the Result.

// Bad
match result {
Ok(value) => value,
Err(_) => return Err("Another error"),
}

// Good
do_something().map_err(|e| "Another error")?