The full guide is at docs/guide_rust.md. Key points:
- Formatting: 2-space indentation, LF line endings, no trailing whitespace, single newline at end of file. Max line
width 120, comment width 80. Enforced by
rustfmt.toml. - Naming:
UpperCamelCasefor types/traits/enum variants,snake_casefor functions/variables/modules,SCREAMING_SNAKE_CASEfor constants. Acronyms as words (TxIdnotTXID). Getters omitget_prefix. - Type safety: newtypes over primitives when semantics differ, enums over booleans, make invalid states
unrepresentable. Derive
Clone,Debug,PartialEq,Eq,Hash,Defaulteagerly. - Error handling: never
.unwrap()or.expect()in library code. Propagate with?. Domain error enums implementDisplay. Lowercase messages without trailing punctuation. Use#[expect]over#[allow]. - Ownership: prefer borrowing over cloning, accept
&strover&String,&[T]over&Vec<T>. Let the caller decide when to clone. - Conversions:
as_(free, borrow),to_(allocates),into_(consumes). ImplementFrom/TryFrom, neverIntodirectly. - Comments: inline comments max 80 chars, 3 lines. Rustdoc summary max 3 lines, don't restate the signature.
Document
# ErrorsforResult-returning functions. - Code segmentation: organise code through modules (in-file or separate files) and naming prefixes. Never use
decorative separator comments (
// ----,// ====,// -- Section --). Latin-1/ISO 8859-1 characters in source files only; no Unicode dashes, arrows, box drawing, or other decoration in comments or identifiers. - Security: never log secrets, custom
Debugfor sensitive types, constant-time comparison for secrets, zeroize after use.
no_std+allocis mandatory for all crates. Every crate uses#![no_std]withextern crate alloc.stdis an optional feature that downstream consumers enable when they need stdlib I/O,Errorimpls, etc.- No networking code in any crate. The SDK provides encoding, decoding, and data types only.
Every crate must follow this feature layout:
[features]
default = []
std = [...]
full = ["std"] # add "serde" here only if the crate implements Serialize/Deserialize
serde = ["dep:serde"] # only if the crate has serde impls
_internal = [] # only if we need to expose internalsNo other features should exist unless there is a pressing justification. _internal is reserved for test and benchmark
support. The serde feature is only added to crates that actually derive or implement Serialize/Deserialize on
public types.
Any shim code that mediates between alloc and std (e.g. pub(crate) use alloc::vec::Vec;) belongs in
crate::prelude.
Every .rs file follows this order:
//
// Copyright (c) 2026-present, The Dash Core developers
// SPDX-License-Identifier: MIT
// See the accompanying file LICENSE or https://opensource.org/license/MIT
//
//! One-line module description.
use crate::some_internal_module;
use some_external_crate;
// ... code ...- Copyright header (5-line block, more if additional attribution needed)
- Blank line
- Module doc (
//!): one line, plus an optional 3-line paragraph for exceptional cases - Blank line
- Internal imports (
use crate::...,use super::...) - Blank line
- External imports (
use some_crate::...) - Blank line
- Code
- Tests: use
rstestfor parametrized and fixture-based tests. - Benchmarks: use
divanas the benchmark harness. - Corpus data: must be JSON5 (
.json5files incorpus/). JSON5 allows comments for annotating test vectors.
pkgs/<name>/
bench/
corpus/
src/
tests/
Cargo.toml
Cargo.toml must set:
[package]
name = "dash-<name>"
[lints]
workspace = trueCrates within this repo must specify path and version:
dash-num = { version = "0.0.0", path = "../num" }All changes must pass before merge. Use full,_internal for the widest coverage.
cargo fmt --check
cargo test --features full,_internal
cargo bench --features full,_internal
cargo clippy --features full,_internal --tests