3Variables and Constants
Every variable declaration in Cryo has three parts: a mutability qualifier (const or mut), a name with an optional type annotation, and an optional initialiser. When the annotation is omitted, the binding's type is inferred from its initialiser.
const name: string = "Cryo"; // immutable binding
mut counter: int = 0; // mutable binding
counter = counter + 1; // reassignment requires `mut`
const greeting = "hello"; // inferred: string
mut total = 3 + 4; // inferred: i32
mut it = arr.iter(); // inferred: the concrete iterator type
Immutable by default. A const binding cannot be reassigned after initialisation. Mutability is opt-in via mut, which makes mutation visible at the declaration site.
mut y: int; // declared without an initialiser; assigned later
Globals. Module-level const declares a true compile-time constant; module-level mut declares mutable global state. Use the latter sparingly.
const VERSION: string = "1.0.0";
mut g_counter: u64 = 0;
Local type inference. The type annotation may be omitted when an initialiser is present; the binding adopts the initialiser's concrete type (
const x = 10;infersi32,mut p = Point { ... };infersPoint). Because the inferred type is the concrete one the initialiser produces - not an erasedimplement Trait- methods on it stay callable, somut it = arr.iter(); it.take(3)...works without naming the iterator type. Inference is purely local: it reads only the initialiser of the same statement, never later uses. A binding with no initialiser therefore still needs an annotation (mut y: int;), and an initialiser that yields no value (void) cannot be inferred (both areE0104). There are still no implicit conversions and no flow- or program-level inference.