Skip to content
CryoCryo home
LanguageFoundations

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; infers i32, mut p = Point { ... }; infers Point). Because the inferred type is the concrete one the initialiser produces - not an erased implement Trait - methods on it stay callable, so mut 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 are E0104). There are still no implicit conversions and no flow- or program-level inference.