We don't maintain a separate style guide but in general try to follow common good practice, write readable and idiomatic code and aim for full test coverage. In addition, this document lists a few decisions we've reached in discussions about specific topics.
We currently always use the latest Rust stable toolchain. Please install it using rustup
:
https://www.rust-lang.org/tools/install
To update stable:
rustup update stable
Apply Rustfmt to new code before committing, using the default configuration or, if present, the repository's rustfmt.toml
file. We run Rustfmt on the stable toolchain.
To install Rustfmt:
rustup component add rustfmt
To run Rustfmt:
cargo fmt
Crates are tested using cargo-clippy; make sure your code does not produce any new errors when running Clippy. If you don't agree with a Clippy lint, discuss it with the team before explicitly adding a #[allow(clippy::<lint>)]
attribute. We run Clippy on the stable toolchain.
To install Clippy:
rustup component add clippy
To run Clippy:
cargo clippy
cargo clippy --all-targets
If the crate being tested also defines features, these two Clippy commands should also be run with each feature enabled.
Don't unwrap Option
s or Result
s, except possibly when:
- locking a mutex,
- spawning a thread,
- joining a thread,
- writing tests or examples
or in other patterns where using them makes the code much simpler and it is obvious at first glance to the reader (even one unfamiliar with the code) that the value cannot be None
/Err
.
In these cases, prefer to use the macro from the unwrap
crate.
Generally avoid detached threads. Give child threads meaningful names.
This can easily be achieved by preferring to create child threads using maidsafe_utilities::thread::named()
.
- it returns a
Joiner
which helps to avoid detached threads - it requires that the child thread is given a name
In impl
s, always put public functions before private ones.
Generally use
statements should be employed to bring names from different modules into scope. However, functions from other modules should not be brought fully into scope. Instead their module should be brought into scope meaning that subsequent usage of the function requires one level of qualification. For example, if we have:
pub mod a {
pub mod b {
pub struct Harbour {}
pub fn bar() {}
}
}
then the normal use
statement to bring these into scope would be:
use a::b::{self, Harbour};
Requiring functions to be module-qualified allows generically-named functions to be disambiguated, particularly given that stuttering is discouraged. For example, encode()
could exist as a function in modules hex
, base32
and base64
. That function shouldn't be named e.g. hex::hex_encode()
, so when we use it, it's clearer to write hex::encode()
rather than just encode()
.
This policy on imports applies to all repositories apart from safe_client_libs, where functions are also fully brought into scope. This is because the safe_client_libs workspace has many instances of functions which if partially qualified would make the code unnecessarily verbose.
We also have an exception for all repositories using the serialisation functions from maidsafe_utilities. These should always be fully brought into scope, since qualifying any of these with serialisation::
only increases verbosity without any gain in clarity.
Use cargo-edit
to update dependencies or keep the Cargo.toml
in the formatting that cargo-edit
uses.
Adding new dependencies to MaidSafe crates in general should be discussed in the team first, except if other MaidSafe crates already have the same dependency. E.g. quick-error and unwrap are fine to use.
The first line of the commit message should have the format <type>/<scope>: <subject>
. For details see the Leaf project's guidelines.