R
RUSTY YELLOW PAGES

;Punctuation


Explanation

; terminates a statement, and — just as importantly in an expression-oriented language — turns what would otherwise be an expression into a statement that evaluates to ().

This is why leaving off the trailing semicolon on a block's last line means "return this value," while adding one means "run this and discard the result." Getting this wrong (an accidental trailing ;) is one of the most common beginner mistakes — a function meant to return a value silently returns () instead, usually caught only by a type error at the call site.

; also appears inside [Type; N] (array type/literal — see square brackets), which is unrelated to its statement-terminator role.

Usage examples

Controlling whether a block returns a value

fn increment(x: i32) -> i32 {
    x + 1 // <- no `;`: this expression IS the function's return value
}

fn increment_wrong(x: i32) -> i32 {
    x + 1; // <- `;` turns this into a statement; the block now returns `()`
}

Restriction: increment_wrong above fails to compile — its body evaluates to (), which doesn't match the declared -> i32 return type.

Handling and propagating errors

An early-return guard clause needs its own ;, while the function's final, successful value must not have one — mixing this up inside a ?-chain is exactly the "silently returns ()" trap the Explanation above describes, and it's easy to hit in a longer function.

struct Config { raw: String }
enum ConfigError { Empty, Io(std::io::Error) }
impl From<std::io::Error> for ConfigError {
    fn from(e: std::io::Error) -> Self { ConfigError::Io(e) }
}
fn parse_config(text: &str) -> Result<Config, ConfigError> {
    Ok(Config { raw: text.to_string() })
}

fn load_config(path: &str) -> Result<Config, ConfigError> {
    let text = std::fs::read_to_string(path)?; // <- `;` ends this statement
    if text.is_empty() {
        return Err(ConfigError::Empty); // <- `;` ends this early-return statement
    }
    // AVOID: a trailing `;` here would make the fn return () instead of Result<Config, _>
    parse_config(&text) // PREFER: no `;` — this expression IS the fn's return value
}

The compiler catches this mistake as a type mismatch (expected Result<Config, ConfigError>, found ()) pointing at the function body — but the more reliable habit is to read a function's last line and ask "is this a statement or the return value?" before deciding whether it needs a ;.

Working with collections

Inside [Type; N] and [value; N], ; separates a type/value from a compile-time length — a completely different grammatical role from statement-termination, disambiguated purely by appearing inside [ ].

let buffer: [u8; 4] = [0; 4]; // <- both `;` here belong to array syntax, not statements
//           ^ type; length      ^ value; repeat count

[0; 4] avoids writing out [0, 0, 0, 0] by hand and, unlike a Vec, the length is part of the type itself — see Arrays vs Vec for when a fixed-size array is the right choice over a growable one.

Explanation

Embedded support: Full

; means exactly the same thing under #![no_std] — statement terminator and array-length separator, resolved purely by the compiler. The "an accidental trailing ; changes what a block returns" trap is, if anything, higher-stakes in a #[no_mangle] extern "C" fn entry point or an interrupt handler: these are called directly by the runtime/linker with no wrapping main and no caller-side application logic to catch a mismatch, so getting the last line's ; wrong there is a raw ABI-shaped bug, not just a caught type error. The [Type; N] array form is, if anything, more central in embedded code than hosted, since a fixed-size array is usually the only buffer available without a heap.

Usage examples

A `#[no_mangle] extern "C"` entry point

#![no_std]
#![no_main]

#[no_mangle]
pub extern "C" fn _start() -> ! {
    init_hardware(); // <- `;` ends this statement
    loop {} // <- no `;` needed: `loop {}` never produces a value to return anyway
}

fn init_hardware() {}

Sizing a fixed DMA buffer with array syntax

static mut DMA_BUFFER: [u8; 256] = [0; 256]; // <- both `;` here belong to array syntax, not statements