1.8Keywords and naming identifiers

Last updated June 11, 2026

Every name in your program (variables so far; functions and types soon) is an identifier, and this lesson covers what identifiers may look like, plus the short list of words you can't have because Rust got there first.

Keywords

A keyword is a word with reserved meaning in the language. You've used six already: fn, let, mut, use, and the value-words true and false (coming in chapter 4). The 2024 edition reserves these:

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

(Plus the lone underscore _, which acts as a special "deliberately unnamed" marker you've already brushed against in _unused.) Don't memorize the table; you'll absorb it by collision. A keyword can't be an identifier, and the compiler is blunt about it:

fn main() {
    let fn = 5;
    println!("{fn}");
}
error: expected identifier, found keyword `fn`
 --> src/main.rs:2:9
  |
2 |     let fn = 5;
  |         ^^ expected identifier, found keyword

For advanced readers

A second tier of words (abstract, become, box, do, final, gen, macro, override, priv, try, typeof, unsized, virtual, yield) is reserved for the future: unusable today, not yet meaning anything. This is editions machinery from lesson 0.12 working as designed, parking words now so claiming them later breaks nothing. And if you ever must use a keyword as a name (say, a field called type in data from elsewhere), raw identifier syntax r#type exists. File under "good to know it exists."

The rules

The hard rules for identifiers are short: letters, digits, and underscores only; it can't start with a digit; and it can't be a keyword. Identifiers are case-sensitive, so score, Score, and SCORE are three different names (defining all three is legal, and a misdemeanor).

The conventions

The rules leave room for chaos, so the community settled conventions, and here Rust does something unusual: the compiler itself nudges you. Variables and functions are written in snake_case: lowercase words joined by underscores. Type names (chapter 10) get UpperCamelCase, and constants (lesson 5.1) get SCREAMING_SNAKE_CASE. Stray from the pattern and you get a warning:

fn main() {
    let playerName = "Ada";
    println!("{playerName}");
}
warning: variable `playerName` should have a snake case name
 --> src/main.rs:2:9
  |
2 |     let playerName = "Ada";
  |         ^^^^^^^^^^ help: convert the identifier to snake case: `player_name`
  |
  = note: `#[warn(non_snake_case)]` (part of `#[warn(nonstandard_style)]`) on by default

The program runs, but the message is clear, and so is the ecosystem's expectation: all Rust code everywhere uses these conventions, which is why code you find online always looks like the code in this course. (Veterans of other languages' brace-and-case wars will recognize what a gift that is.)

Beyond case, good names are a judgment call, and the judgment improves with a few principles. Make the length of a name proportional to its importance: a loop counter alive for two lines can be i; a value used across a whole function deserves a word; something central to the program deserves a good one. Name the thing, not the type (elapsed_seconds beats the_number). And don't abbreviate to save four keystrokes that cost every future reader a guess.

A field guide, in the spirit of an old learncpp.com table:

DeclarationVerdict
let elapsed_seconds = 4;conventional
let s = 4;fine in a two-line scrap, cryptic anywhere else
let elapsedSeconds = 4;works, but the compiler will tut (snake case, please)
let ELAPSED = 4;shouting is for constants (lesson 5.1)
let _elapsed = 4;the underscore says "unused on purpose," so only when it is
let seconds_elapsed_since_the_program_started_running = 4;technically legal, like a 40-character password
let 2fast = 4;illegal: starts with a digit
let player name = 4;illegal: spaces can't appear in identifiers

Best practice

Name things in snake_case, descriptively, as if the reader knows nothing. The three seconds you spend choosing remaining_attempts over ra are repaid every single time anyone (including you) reads the line.

Quiz time

Question #1

Judge each: conventional, legal-but-unconventional, or illegal?

a) let user_count = 10; b) let UserCount = 10; c) let 3d_points = 10; d) let match = 10; e) let x = 10; (as a program's central value, used in 30 places)

Show solution

a) Conventional. b) Legal but unconventional for a variable; that casing belongs to types, and the compiler warns. c) Illegal: identifiers can't start with a digit. d) Illegal: match is a keyword. e) Legal and conventionally shaped, but a bad name for something used 30 times; one-letter names are for tiny scopes.

Question #2

Predict what the compiler says about this program (it does compile):

fn main() {
    let finalScore = 95;
    println!("{finalScore}");
}
Show solution

A non_snake_case warning: variable finalScore should have a snake case name, with the help line suggesting final_score. The program still compiles and prints 95.

Names sorted. Next, a quick lesson on the space between the names, and the tool that ends every argument about it.