rust-lang/rust
Patterns and conventions
A pragmatic guide to the conventions that recur across rust-lang/rust. The official style guide is the in-tree Rust style guide and the rustfmt config; this page captures the project-internal conventions on top.
Crate naming
rustc_*— internal compiler crates. Not stable, not for public consumption. Live undercompiler/.rustc_*_impl— the heavy implementation behind a thin facade (rustc_drivervs.rustc_driver_impl,rustc_query_systemvs.rustc_query_impl). Allows incremental compilation by isolating churn.- Plain names (
core,alloc,std,proc_macro,test) — public standard library crates. Live underlibrary/.
File and module layout
- Each crate has a
src/lib.rs(orsrc/main.rsfor binaries) that listsmods andpub uses. - The convention is
pub use foo::Bar;at the lib root rather than re-exporting throughout. This keepsuse rustc_middle::ty::Ty;working regardless of whereTyis implemented. - Submodules under
src/mirror logical structure, not alphabetical order.
// tidy-alphabetical-* markers
Many lists in this repo (workspace members, crates in dependency lists, lints in static arrays) are kept alphabetical and enforced by tidy. The marker pair:
# tidy-alphabetical-start
"compiler/rustc",
"src/build_helper",
…
# tidy-alphabetical-end…tells tidy to enforce alphabetical ordering of the lines in between. Honor the markers.
Diagnostics
Compiler errors and warnings go through the rustc_errors crate. The convention is:
- Fluent messages — user-facing strings live in
.ftlfiles under each crate'smessages/directory, not inline. - Diagnostic structs — derive
#[derive(Diagnostic)](orSubdiagnostic,LintDiagnostic) on a struct that holds the data the message needs. - Emit via
tcx.dcx()or theSession— neverprintln!an error.
Example pattern:
#[derive(Diagnostic)]
#[diag(borrowck_borrow_through_x)]
struct BorrowThroughX {
#[primary_span]
span: Span,
name: Symbol,
}
tcx.dcx().emit_err(BorrowThroughX { span, name });The corresponding .ftl entry lives in compiler/rustc_borrowck/messages.ftl.
tracing over println!
Use tracing::{debug, info, trace, warn, error} and instrumented spans (#[instrument]) for compiler logging; println! ends up in user output. See Debugging for RUSTC_LOG filter syntax.
Error handling
ErrorGuaranteed— a zero-sized token returned bydcx().emit_err(...). Many APIs require it as evidence that an error has been emitted before they take a fallible path. Don't construct it manually.Result<T, ErrorGuaranteed>— the common return type for "this might bail with a diagnostic".- Delayed bugs —
dcx.delayed_bug(msg)records an invariant that must be flagged via a real error, but the real error happens later. Use this to assert "this code path is only reached if we already emitted an error somewhere".
TyCtxt and queries
Almost everything in the compiler reaches global state via TyCtxt. Conventions:
- Pass
tcx: TyCtxt<'tcx>rather than&TyCtxt. - Compute things via queries (
tcx.def_kind(def_id),tcx.type_of(def_id)). Queries are memoized and form the basis of incremental compilation. - New queries are added in
compiler/rustc_middle/src/query/mod.rs; their providers are registered inrustc_query_impl.
Lifetimes named 'tcx
The lifetime 'tcx runs through the type system: Ty<'tcx>, TyCtxt<'tcx>, Body<'tcx>. It is the lifetime of the arena allocations behind TyCtxt. When you see <'tcx> on a struct, it's almost always this.
Arena allocation
Heavy compiler data structures (types, AST nodes, MIR bodies) are arena-allocated for speed. You'll see tcx.arena.alloc(...) and per-IR arena types (tcx.hir_arena, etc.). Don't Box up these things — use the arena.
Cell types
The compiler uses several specialized cell types from rustc_data_structures:
OnceCell/OnceLockfor write-once globalsFxHashMap/FxHashSet(FxHash) — faster, deterministic alternatives toHashMapIndexVec<I, T>for vectors keyed by a typed integer indexBitSet,SparseBitMatrixfor dataflow
Use these instead of std equivalents in compiler code; they're tuned for rustc's workload.
Feature gates
Unstable features go through compiler/rustc_feature/. Each unstable feature has:
- A name (
#![feature(my_feature)]) - A tracking issue (
#NNNNN) - A status:
active,accepted, orremoved - An entry under
compiler/rustc_feature/src/unstable.rs(or stable/removed)
-Z flags work the same way but go through compiler/rustc_session/src/options.rs.
Tidy-banned patterns
A few things tidy will reject:
- New external crates.io dependencies not in
src/tools/tidy/src/extdeps.rs - New
pub usepaths that aren't either documented or marked unstable - Files larger than the size threshold in tidy's
large_filescheck - Trailing whitespace, mixed tabs/spaces
License headers and REUSE.toml
License metadata is centralized in REUSE.toml following the REUSE specification. Most source files don't need an SPDX header — the central file declares licensing for whole directories.
Rustfmt
The repo uses an opinionated rustfmt.toml (more conservative than the rustfmt default). Run ./x fmt before pushing. Reviewers may ask for non-format-driven changes ("can you split this expression for readability") that rustfmt won't enforce.
PR titles
Prefix the PR title with the area being touched, in lowercase: borrowck: fix message ordering, bootstrap: stop downloading X twice. Tooling consumes these prefixes for changelog generation.
Cross-cutting "do not bloat" attitude
Compile time and binary size are first-class concerns:
- Avoid adding heavy dependencies (
synandquoteare only used in proc-macro code, not in rustc itself) - Don't
derive(Debug, Clone, Hash, PartialEq, Eq)reflexively — these add code - Watch
-Zprint-type-sizeswhen adding new fields to hot types likeTy,TyKind, or query keys - Be careful with
where Self: Sizedand trait-object-using indirection — devirtualization matters
These are rules of thumb the team brings up in review. Treat them as defaults, not absolutes.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.