Rationale and Goals
As every Rust programmer knows, the language has many powerful features, and there are often several patterns which can express the same idea. Also, as every professional programmer comes to discover, code is almost always read far more than it is written.
Thus, we choose to use a consistent set of idioms throughout our code so that it is easier to read and understand for both existing and new contributors.
Unsafe and Platform-Dependent conditional compilation
Avoid unsafe
Rust
One of the main reasons to use Rust as an implementation language is its strong memory safety
guarantees; Almost all of these guarantees are voided by the use of unsafe
. Thus, unless there is
an excellent reason and the use is discussed beforehand, it is unlikely HoraeDB will accept patches
with unsafe
code.
We may consider taking unsafe code given:
- performance benchmarks showing a very compelling improvement
- a compelling explanation of why the same performance can not be achieved using
safe
code - tests showing how it works safely across threads
Avoid platform-specific conditional compilation cfg
We hope that HoraeDB is usable across many different platforms and Operating systems, which means we put a high value on standard Rust.
While some performance critical code may require architecture specific instructions, (e.g.
AVX512
) most of the code should not.
Errors
All errors should follow the SNAFU crate philosophy and use SNAFU functionality
Good:
- Derives
Snafu
andDebug
functionality - Has a useful, end-user-friendly display message
|
|
Bad:
|
|
Use the ensure!
macro to check a condition and return an error
Good:
- Reads more like an
assert!
- Is more concise
|
|
Bad:
|
|
Errors should be defined in the module they are instantiated
Good:
- Groups related error conditions together most closely with the code that produces them
- Reduces the need to
match
on unrelated errors that would never happen
|
|
Bad:
|
|
The Result
type alias should be defined in each module
Good:
- Reduces repetition
|
|
Bad:
|
|
Err
variants should be returned with fail()
Good:
|
|
Bad:
|
|
Use context
to wrap underlying errors into module specific errors
Good:
- Reduces boilerplate
|
|
Bad:
|
|
Hint for Box<dyn::std::error::Error>
in Snafu:
If your error contains a trait object (e.g. Box<dyn std::error::Error + Send + Sync>
), in order
to use context()
you need to wrap the error in a Box
, we provide a box_err
function to help do this conversion:
|
|
Each error cause in a module should have a distinct Error
enum variant
Specific error types are preferred over a generic error with a message
or kind
field.
Good:
- Makes it easier to track down the offending code based on a specific failure
- Reduces the size of the error enum (
String
is 3x 64-bit vs no space) - Makes it easier to remove vestigial errors
- Is more concise
|
|
Bad:
|
|
Leaf error should contains backtrace
In order to make debugging easier, leaf errors in error chain should contains a backtrace.
|
|
Tests
Don’t return Result
from test functions
At the time of this writing, if you return Result
from test functions to use ?
in the test
function body and an Err
value is returned, the test failure message is not particularly helpful.
Therefore, prefer not having a return type for test functions and instead using expect
or
unwrap
in test function bodies.
Good:
|
|
Bad:
|
|
Thanks
Initial version of this doc is forked from influxdb_iox, thanks for their hard work.