Syntax, Comments & Style
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against Rust 1.99.0 and the 2024 edition, checked on 2026-10-06. It describes how Rust source text is put together — the tokens the compiler sees, the comment forms, the difference between items and statements, attributes — and the conventions the community follows for naming and layout. The operators are listed in Types & Values and Standard Traits & Operator Overloading.
Source files and tokens
Rust source files are UTF-8 text with the .rs extension. The lexer turns them into tokens: keywords,
identifiers, literals, lifetimes ('a), punctuation (::, ->, =>, #, ..= …) and delimiters ((),
[], {}). Whitespace — including newlines — only separates tokens; Rust is not indentation-sensitive. A file may
start with a shebang line (#!/usr/bin/env ...), which is ignored.
Source: The Rust Reference: Tokens
Keywords
The Reference sorts keywords into three groups.
Strict keywords can only be used as keywords:
as, async, await, break, const, continue, crate, dyn, else, enum, extern, false, fn, for,
if, impl, in, let, loop, match, mod, move, mut, pub, ref, return, self, Self, static,
struct, super, trait, true, type, unsafe, use, where, while.
Reserved keywords are not used yet but cannot be identifiers, so the language can adopt them later without breaking code:
abstract, become, box, do, final, gen (2024 edition), macro, override, priv, try (2018 edition),
typeof, unsized, virtual, yield.
Weak keywords have a special meaning only in certain positions and are otherwise normal identifiers:
-
union— only when declaring a union (union U { ... }); -
'static— the static lifetime, which cannot be used as a generic lifetime parameter name; -
safe— only insideunsafe externblocks (2024 edition); -
raw— only in&raw const/&raw mut(1.82).
Source: The Rust Reference: Keywords
Identifiers and raw identifiers
Identifiers start with a letter or _ followed by letters, digits and _. Non-ASCII identifiers (Unicode
XID_Start/XID_Continue, NFC-normalised) are allowed, but rustc warns about confusable or mixed-script names, and
the API Guidelines recommend ASCII for public APIs. _ alone is not an identifier but a pattern that discards a value.
A raw identifier r#name lets you use a keyword as a name. It exists mainly for interoperability between
editions: when 2018 made async and try keywords, older crates exporting a function called try stayed callable
as r#try, and the 2024 edition’s new gen keyword works the same way.
// A crate written for an older edition might export a function named `gen`;
// in a 2024 crate it can still be defined and called with the raw form.
fn r#gen(seed: u64) -> u64 {
seed.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1)
}
struct Query {
r#type: String, // `type` is a keyword; the field is still called `type`
}
fn main() {
let q = Query { r#type: "title".to_string() };
println!("{} {}", q.r#type, r#gen(42));
}
Source: The Rust Reference: Identifiers
Literals
| Kind | Examples | Notes |
|---|---|---|
Integer |
|
|
Float |
|
default |
Character |
|
a Unicode scalar value, 4 bytes |
String |
|
UTF-8; a |
Raw string |
|
no escapes; add |
Byte, byte string |
|
|
C string |
|
|
Boolean |
|
Source: The Rust Reference: Literals
fn main() {
let isbn_prefix = 978_u16;
let flags = 0b0000_0101_u8;
let mask = 0xFF_u8;
let price = 24.99_f64;
let path = r"C:\shelf\books.json";
let json = r#"{"title": "Dune"}"#;
let method = b"GET";
let c_title = c"Dune";
println!("{isbn_prefix} {flags} {mask} {price} {path} {json} {method:?} {c_title:?}");
}
Comments and doc comments
Rust has two ordinary comment forms, both ignored by the compiler:
-
line comments,
//to the end of the line (the idiomatic form); -
block comments,
/* ... */, which nest, so commenting out a block that contains comments is safe.
Doc comments are syntax for the doc attribute, and rustdoc renders them as Markdown:
| Form | Equivalent | Documents |
|---|---|---|
|
|
the item that follows (outer doc comment) |
|
|
the enclosing item — usually the crate in |
//! Utilities for formatting catalogue entries (a crate called `shelf_fmt`).
//!
//! This inner doc comment documents the enclosing module.
/// Formats a title and year as `Title (Year)`.
///
/// # Examples
///
/// ```
/// assert_eq!(shelf_fmt::label("Dune", 1965), "Dune (1965)");
/// ```
pub fn label(title: &str, year: u16) -> String {
// An ordinary comment: not part of the documentation.
/* Block comments /* nest */ too. */
format!("{title} ({year})")
}
fn main() {
assert_eq!(label("Dune", 1965), "Dune (1965)");
}
Source: The Rust Reference: Comments
Code blocks in doc comments are compiled and run as tests by cargo test; see
Documentation with rustdoc for sections, intra-doc
links and doctest attributes.
Items, statements and expressions
A Rust source file is a list of items: functions (fn), types (struct, enum, union, type), trait and
impl blocks, const and static declarations, modules (mod), use declarations, extern blocks, macro
definitions and extern crate. Items are declared at compile time; their order in the file does not matter, and they
can also appear inside a function body, where they are only visible to that block.
Inside function bodies, code is a sequence of statements:
-
letstatements introduce variables; -
item declarations;
-
expression statements — an expression followed by
;, evaluated for its side effects; -
empty statements (
;).
Nearly everything else is an expression that produces a value, including blocks, if, match and loop. A block
evaluates to its final expression if it has no trailing semicolon. That rule is covered in
Functions & Expressions.
fn main() {
// An item declared inside a function: visible only in this block.
fn shout(s: &str) -> String {
s.to_uppercase()
}
let title = "dune"; // let statement
shout(title); // expression statement (result discarded)
let loud = { let t = shout(title); t + "!" }; // block expression: value of its tail
println!("{loud}");
}
Attributes
Attributes are metadata attached to items, statements and expressions, written #[name], #[name = "value"]
or #[name(args)]. An outer attribute (#[...]) applies to the thing that follows; an inner attribute
(#![...]) applies to the enclosing item and must come first in it — typically at the top of lib.rs or main.rs
to configure the whole crate. The main families:
| Family | Examples |
|---|---|
Conditional compilation |
|
Testing |
|
Derive |
|
Lint levels |
|
Code generation |
|
Diagnostics |
|
Unsafe attributes (2024) |
|
Tool attributes |
|
Crate-level |
|
#![warn(missing_docs)]
//! A crate whose public items must all be documented.
/// The default loan period in days.
pub const LOAN_DAYS: u16 = 21;
/// Returns the due day for a loan that starts on `day`.
#[must_use]
pub fn due_day(day: u16) -> u16 {
day + LOAN_DAYS
}
#[cfg(test)]
mod tests {
#[test]
fn three_weeks() {
assert_eq!(super::due_day(1), 22);
}
}
fn main() {
let _ = due_day(1);
}
Source: The Rust Reference: Attributes
Naming conventions
The compiler enforces naming conventions with warn-by-default lints (non_snake_case, non_camel_case_types,
non_upper_case_globals). The API Guidelines spell them out:
| Item | Convention | Example |
|---|---|---|
Crates |
|
|
Modules, functions, methods, local variables, fields, macros |
|
|
Types, traits, enum variants |
|
|
Type parameters |
short |
|
Lifetimes |
short lowercase |
|
Constants and statics |
|
|
Features |
no placeholder words such as |
|
Acronyms count as one word in UpperCamelCase (Uuid, HttpClient, not UUID or HTTPClient). Conversion
methods follow a cost convention: as_ is free and borrowed (as_str), to_ is expensive or allocates
(to_string), and into_ consumes self (into_bytes). Getters do not use a get_ prefix (len(), not
get_len()), and iterator methods are named iter, iter_mut and into_iter.
Source: Rust API Guidelines: Naming
The Rust Style Guide and rustfmt
The official Rust Style Guide defines the default formatting, and rustfmt (run as cargo fmt) applies it. The
most visible rules:
-
four spaces of indentation, no tabs; a maximum width of 100 characters;
-
opening braces on the same line as the item (
fn main() {), trailing commas in multi-line lists; -
whereclauses, long function signatures and method chains broken one element per line; -
imports sorted and grouped; one blank line at most between items.
cargo fmt # format the whole package in place
cargo fmt --check # CI: exit with an error if anything would change
Source: rustfmt: Quick start
A project can tune rustfmt with a rustfmt.toml, but most projects keep the defaults so that all Rust code looks
the same. Since the 2024 edition, the style edition is versioned separately: style_edition = "2024" (the default
for 2024 crates) changes, among other things, how version-sorted identifiers are ordered in imports. Details are in
Tooling: Clippy, rustfmt &
rust-analyzer.
Source: The Rust Style Guide