Rust Syntax

Rust syntax centers on the main function, semicolon-terminated statements, and the powerful idea that blocks can themselves be expressions.

The main Function

Every executable Rust program needs exactly one function named main, which is the entry point the operating system calls to start your program. Function bodies are wrapped in curly braces, and Rust uses braces (not indentation) to define blocks of code.

The minimal Rust program

fn main() {
    println!("Program started");
}

Statements and Semicolons

Rust is an expression-based language, but individual statements are still terminated with a semicolon, similar to C, Java, or JavaScript. A statement performs an action and does not itself produce a usable value. Forgetting a semicolon where one is required is one of the most common early mistakes new Rust developers make.

Statements ending in semicolons

fn main() {
    let a = 5;
    let b = 10;
    let sum = a + b;
    println!("Sum: {}", sum);
}

Blocks as Expressions

A block, delimited by curly braces, is itself an expression in Rust and can evaluate to a value. If the last line inside a block has no trailing semicolon, that line's value becomes the value of the whole block. This is why Rust's if, match, and loop constructs can directly return values, unlike in most C-family languages.

A block returning a value

fn main() {
    let result = {
        let x = 3;
        let y = 4;
        x * y // no semicolon: this is the block's value
    };
    println!("Block evaluated to: {}", result);
}

This same rule applies to if expressions. Because if is an expression, both branches must produce the same type, and the value can be assigned directly to a variable without needing a separate mutable variable declared beforehand.

if as an expression

fn main() {
    let temperature = 15;
    let description = if temperature > 20 {
        "warm"
    } else {
        "cool"
    };
    println!("It feels {}", description);
}
  • Curly braces { } define blocks and function bodies, not indentation
  • Statements end in a semicolon and produce no value
  • Expressions produce a value and can be used anywhere a value is expected
  • Omitting the semicolon on a block's final line makes it the block's return value
  • Function bodies are themselves blocks, so the same rule lets you omit 'return' for the final value
ConstructIs it an expression?
if / elseYes, when both branches produce the same type
matchYes, every arm must produce the same type
loopYes, via the break keyword with a value
for / whileNo, they always evaluate to the unit type ()
A bare block { }Yes, if the last line has no semicolon
Note: Adding a semicolon after the last line of a block silently turns its value into (), the unit type, which is a frequent source of confusing type errors for beginners.
Note: When the compiler complains about mismatched types around an if/else or match, check first whether a stray semicolon turned one branch into ().

Exercise: Rust Syntax

Which function must every executable Rust program contain?