| name | rust-style |
| description | Rocky's Rust coding style — let-else early returns, shadowing, newtypes, enums over bools, no wildcard matches, no noise comments. Use when writing or reviewing Rust code in any engine crate. |
Rocky Rust style
Source: partially vendored from davidbarsky/8fae6dc45c294297db582378284bd1f2 @ 191b2ee46088920de97d682561e2abd1edd64a42 (SKILL-2.md).
What this skill is and isn't
This is a cherry-pick of Barsky's rust-style gist, not a verbatim vendor. Barsky himself flags that his gist is personal preference. The rules below are the ones that fit Rocky's existing code; the rules that conflict with idiomatic Rocky are deliberately omitted.
Rules deliberately dropped from the upstream gist
| Upstream rule | Why Rocky doesn't enforce it |
|---|
"Use for loops, not iterator chains" | Rocky uses iter().filter().map().collect() freely throughout the codebase — it's idiomatic Rust and the codebase is already consistent with it. Swapping to mutable-accumulator loops would be a large and contentious rewrite. |
"Avoid the matches! macro" | matches! is the standard Rust idiom for boolean variant checks. Rocky uses it; removing it would produce more verbose code, not clearer code. |
| "Always use explicit destructuring for struct field access" | Too strict for a 23-crate workspace. Dot-access is fine for ad-hoc reads; reserve destructuring for match arms and when all fields are consumed. |
The rules below are the ones that are enforced.
Early returns: use let ... else
Use let ... else to extract values and exit early on failure. This keeps the happy path unindented.
let Some(user) = get_user(id) else {
return Err(Error::NotFound);
};
let Ok(session) = user.active_session() else {
return Err(Error::NoSession);
};
if let Some(user) = get_user(id) {
if let Ok(session) = user.active_session() {
} else {
return Err(Error::NoSession);
}
} else {
return Err(Error::NotFound);
}
let Some(value) = maybe_value else { continue };
let Ok(parsed) = input.parse::<i32>() else { continue };
Pattern matching: use if let only for short, no-else cases
if let Some(callback) = self.on_change {
callback();
}
let Some(config) = load_config() else {
return default_config();
};
match result {
Ok(value) => process(value),
Err(Error::NotFound) => use_default(),
Err(e) => return Err(e),
}
Variable naming: shadow, don't rename
Shadow variables through transformations. Avoid prefixes like raw_, parsed_, trimmed_.
let input = get_raw_input();
let input = input.trim();
let input = input.to_lowercase();
let input = parse(input)?;
let raw_input = get_raw_input();
let trimmed_input = raw_input.trim();
let lowercase_input = trimmed_input.to_lowercase();
let parsed_input = parse(lowercase_input)?;
Comments: minimise noise
Keep comments to what the code can't say for itself.
- No inline comments explaining what obvious code does.
- No section headers or dividers (
// --- Section ---).
- No TODO comments — use the plans backlog.
- No commented-out code — use
git log.
- Exception: doc comments (
///) on public items are required and follow the rust-doc skill. SAFETY: comments on unsafe blocks are required and follow the rust-unsafe skill.
if user.is_valid() {
user.touch();
}
fn helper() { }
if user.is_valid() {
user.touch();
}
fn helper() { }
Type safety: newtypes over raw strings
Wrap strings in newtypes to add semantic meaning and prevent mixing. Rocky already uses this pattern for SQL identifiers and catalog/schema/table names — see rocky-sql/src/validation.rs.
struct TenantId(String);
struct CatalogName(String);
fn create_catalog(tenant: TenantId, catalog: CatalogName) { }
fn create_catalog(tenant: String, catalog: String) { }
When to not create a newtype: transient locals that never escape a function, or values that are immediately passed through unchanged. Don't cargo-cult this rule.
Type safety: enums over bools
Use enums with meaningful variant names instead of bool parameters.
enum Visibility {
Public,
Private,
}
fn create_repo(name: &str, visibility: Visibility) { }
fn create_repo(name: &str, is_public: bool) { }
enum RefreshMode {
FullRefresh,
Incremental,
}
fn run_pipeline(full: bool) { }
Pattern matching: never wildcard-match an enum you own
Always match all variants explicitly so the compiler errors when a variant is added. This is load-bearing for Rocky because MaterializationStrategy, PipelineType, and the various *Output enums all grow over time and we want the compiler to find every site that needs updating.
match strategy {
MaterializationStrategy::FullRefresh => handle_full(),
MaterializationStrategy::Incremental => handle_incremental(),
MaterializationStrategy::Merge => handle_merge(),
MaterializationStrategy::MaterializedView => handle_mv(),
MaterializationStrategy::DynamicTable => handle_dt(),
MaterializationStrategy::TimeInterval => handle_ti(),
}
match strategy {
MaterializationStrategy::FullRefresh => handle_full(),
_ => handle_other(),
}
Exception: wildcard-matching an enum you don't own (e.g. std::io::ErrorKind, which is #[non_exhaustive]) is fine and often required. The rule is about enums defined inside the Rocky workspace.
If a wildcard seems unavoidable for a Rocky-owned enum, stop and reconsider — it usually means a variant-specific case was overlooked.
Code navigation: prefer rust-analyzer LSP
When searching or navigating Rust code in the 23-crate workspace, prefer LSP operations over raw text search — they respect type resolution and paths:
goToDefinition — find where a symbol is defined
findReferences — find all references (respects re-exports)
hover — type info and documentation
documentSymbol — all symbols in a file
goToImplementation — find trait implementations (especially useful for the Adapter trait family)
For structural refactors, see the rust-analyzer-ssr skill.
Related skills
rust-doc — RFC 1574 doc comment conventions for public items
rust-error-handling — thiserror (lib) vs anyhow (bin) decision tree
rust-async-tokio — Tokio, #[async_trait], AIMD concurrency
rust-unsafe — SAFETY: comment conventions for the engine's few unsafe sites (mmap, a repr(transparent) cast, serialised test env mutation)
rust-clippy-triage — playbook when cargo clippy -D warnings fires