Syntax#

Rust’s grammar is C-shaped with curly braces, semicolons, and explicit blocks; on top of that sit pattern-matching arms, ? propagation, lifetime markers, and attributes (#[...]). Source files start with module declarations (mod, use), then the body. rustfmt is the only formatter; never argues style.

/types`; for operators, see Operators.

fn greet(name: &str) {
    println!("hello, {name}");
}

fn main() {
    greet("operator");
}

Comments#

Line // and block /* ... */ (block comments do nest). Doc comments (/// for the next item, //! for the enclosing module) produce rustdoc output.

// ordinary line comment
/* block /* nested! */ */

/// Returns the operator's preferred greeting.
///
/// # Example
/// ```
/// assert_eq!(greet("op"), "hello, op");
/// ```
fn greet(name: &str) -> String { format!("hello, {name}") }

//! crate-level documentation goes at the top of `lib.rs`

Identifiers#

Unicode letters, digits, and underscores; cannot start with a digit. Convention: snake_case for functions and variables, CamelCase for types and traits, UPPER_SNAKE for constants, _leading_underscore for “intentionally unused”.

let user_count = 0;
const MAX_RETRY: u32 = 5;
struct HttpClient;
trait Listener {}
let _unused = compute();           // silences the "unused" lint

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: abstract become box do final macro override priv try typeof unsized virtual yield.

Literals#

let n: i32  = 42;
let big     = 1_000_000;          // separators allowed
let x       = 0xff_u32;            // suffix sets the type
let o       = 0o755;
let b       = 0b1010_0001;
let pi      = 3.14_f64;
let e       = 6.022e23_f64;

let s: &str = "hello";             // string slice (UTF-8)
let owned   = String::from("hi");
let raw     = r"no \n escapes";    // raw string
let multi   = "this
spans lines";

let c: char = 'A';                 // UTF-32 code point
let b: u8   = b'A';                // byte literal
let bs      = b"bytes";            // byte string

let t       = true;
let n: i32  = 0;
let none: Option<i32> = None;      // Option is the operator's "nullable"

Statements#

Most Rust constructs are expressions. A trailing ; turns an expression into a statement (suppressing its value); the last expression in a block (without ;) is the block’s value.

let x = 1 + 2;                   // statement
let y = {
    let n = 10;
    n * 2                         // expression value of the block
};
// y == 20

fn double(n: i32) -> i32 {
    n * 2                         // implicit return; no `;`
}

Expressions#

Almost everything: arithmetic, function calls, blocks, if, match, loop, range (a..b), closures (|x| x+1), the try operator (?), method chains, and macro invocations.

let n = if x > 0 { x } else { -x };
let label = match n % 2 { 0 => "even", _ => "odd" };
let doubled: Vec<i32> = (0..5).map(|i| i * 2).collect();

let path = std::env::var("HOME")?;        // ? early-returns the Err

Attributes#

Annotations on items (#[attr]) or inner attributes on the enclosing item (#![attr]). Used for metadata, conditional compilation, derives, and lints.

#[derive(Debug, Clone)]
struct User { name: String }

#[allow(dead_code)]
fn experimental() {}

#[cfg(target_os = "linux")]
fn platform_probe() {}

#![warn(missing_docs)]              // applies to the whole crate

Modules#

A crate is the compilation unit (a binary or library); a module is a namespace within it. mod name; declares a module; use path::item; brings names into scope.

mod scan;                           // pulls in scan.rs or scan/mod.rs

use std::collections::HashMap;
use crate::scan::Probe;

See Runtime for the module / crate / workspace model.

References#