Open-Source Wikis

/

ripgrep

/

How to contribute

/

Patterns and conventions

BurntSushi/ripgrep

Patterns and conventions

ripgrep's coding style is consistent across crates. New code should match.

Formatting

rustfmt.toml is short:

max_width = 79
use_small_heuristics = "Max"

The 79-column line limit is unusual for modern Rust but enforced everywhere. cargo fmt is run in CI.

Edition and MSRV

  • edition = "2024" in every workspace Cargo.toml.
  • rust-version = "1.85" for the binary; library crates may have older MSRVs declared in their own Cargo.toml.

Error handling

  • Application code (the rg binary) uses anyhow::Result<T> for fallible functions and ? for propagation. See crates/core/main.rs.
  • Library code (the grep-* crates) defines its own error types using plain enums with #[derive(Debug)] and a Display impl. Examples: grep_regex::Error (crates/regex/src/error.rs), globset::Error (crates/globset/src/lib.rs), ignore::Error (crates/ignore/src/lib.rs).
  • Library errors do not depend on anyhow or thiserror. The convention is hand-rolled, partly to keep dependency counts low.

Logging

  • log for crate-level diagnostics. The rg binary configures it via crates/core/logger.rs.
  • err_message! and eprintln_locked! (defined in crates/core/messages.rs) are used for user-facing stderr messages. They acquire a global lock so concurrent worker threads do not interleave their output.

Documentation

  • #![deny(missing_docs)] is enabled at the top of every public library crate (crates/searcher/src/lib.rs, crates/printer/src/lib.rs, crates/regex/src/lib.rs, crates/matcher/src/lib.rs, crates/pcre2/src/lib.rs, crates/ignore/src/lib.rs). Public items must have /// doc comments.
  • crates/grep is a re-export facade and its lib.rs is intentionally tiny.
  • The CLI binary uses /*! ... */ module-level docs; flags carry their own doc strings via the Flag trait (Flag::doc_short and Flag::doc_long).

CLI flag definitions

Every flag is a type that implements trait Flag (defined in crates/core/flags/mod.rs). The Flag::update method receives a parsed value and updates LowArgs. The conventions:

  • One flag = one type. Negations (--no-foo) live on the same type via Flag::name_negated.
  • Doc strings are static strings written in mdoc style. They feed the man page and --help output.
  • The flag's category (Category::Input, Search, Filter, Output, OutputModes, Logging, OtherBehaviors) controls where it appears in rg --help and the man page.
  • The full list of flag definitions is crates/core/flags/defs.rs (235K of mostly hand-written tables).

When adding a flag:

  1. Define a struct in crates/core/flags/defs.rs with Debug and the Flag trait impl.
  2. Add the resolved field to LowArgs (crates/core/flags/lowargs.rs).
  3. If the flag affects searching, plumb it through HiArgs (crates/core/flags/hiargs.rs).
  4. Register the new flag in the FLAGS slice at the bottom of defs.rs.
  5. Add at least one integration test exercising both the flag and its negation.

Imports and modules

  • use std::{...} style preferred (grouped imports).
  • Crate names use kebab-case in Cargo (grep-regex) and snake_case in code (grep_regex).
  • Module-internal helpers live in pub(crate)-visible modules; only the items that should be externally usable are pub.

Performance

  • The repository is performance-sensitive. Avoid allocations in hot paths.
  • Hot search loops in crates/searcher/src/searcher/{core,glue}.rs are written to compile down to tight code; do not refactor for "elegance" without benchmarks.
  • The bstr crate is used heavily because most byte slices are not guaranteed UTF-8. Prefer bstr traits over str::from_utf8 in performance-sensitive code.

Concurrency

  • ripgrep does not use tokio or any async runtime.
  • crossbeam channels and std::thread provide all parallelism.
  • WalkParallel (crates/ignore/src/walk.rs) owns the thread pool; everything else is per-thread.
  • For shared mutable state, prefer std::sync::atomic over Mutex. Existing examples: matched: AtomicBool in crates/core/main.rs::search_parallel.

Naming

  • File names are snake_case Rust source: gitignore.rs, default_types.rs, line_buffer.rs.
  • Type names follow Rust conventions: WalkBuilder, RegexMatcher, StandardSink.
  • Builders end in Builder and produce a Default::default()-initialised type via build().
  • Internal modules sometimes use single words (ast, ban, strip in crates/regex/src/). These are intentional; keep them short.

When in doubt

Read the surrounding code. Andrew Gallant's style is consistent across crates spanning a decade; the closest neighbour is almost certainly the right model.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Patterns and conventions – ripgrep wiki | Factory