Rust Language Basics

Rust can feel strict when coming from a dynamic language: conditions must be Boolean, bindings are immutable by default, and the compiler tracks how values are used. Those constraints make more program behavior visible before execution.

Bindings and Expressions

A binding created with let is immutable unless it is marked mut:

let width = 4;
let mut height = 3;
height = 5;

Shadowing creates a new binding with the same name. It differs from mutation because the new binding may have a different type while the earlier binding remains unchanged within its own scope.

Many Rust constructs are expressions. An if can therefore produce a value, and a function can return its final expression without return:

fn larger(a: i32, b: i32) -> i32 {
    if a > b { a } else { b }
}

The absence of a trailing semicolon is significant here: adding one turns the expression into a statement whose value is ().

Data and Control Flow

Rust has scalar types such as integers, floating-point numbers, Boolean values, and Unicode scalar-value characters. Tuples group a fixed set of possibly different types; arrays contain a fixed number of values of one type. Vectors are used when the sequence must grow or shrink.

Control flow includes if, loop, while, for, and match. A match must cover every possible case, which makes it especially useful with enums:

match direction {
    Direction::Left => turn_left(),
    Direction::Right => turn_right(),
}

A loop can return a value through break, while for is the natural choice for consuming or borrowing values from an iterator.

Building Types

Structs give names to fields that belong together. Enums describe a value that may be one of several variants. Methods are defined in impl blocks, and associated functions without a self parameter are commonly used as constructors.

These language features interact with Rust ownership and borrowing, which determines whether an operation moves, reads, or mutates a value.