Skip to content

Variables and Bindings

Cerune binds a name to a value. Each binding has a type determined when it is declared. Reassignment is not allowed by default. Add mut to allow reassignment with another value of the same type.

count: i64 = 40;
mut total: i64 = count;
total = total + 2;
print(total); // 42
Syntax Meaning
name: T = value; Declare a binding of type T that cannot be reassigned
mut name: T = value; Declare a binding of type T that permits reassignment
name = value; Reassign an existing mut binding

Do not repeat the type annotation when assigning to an existing binding. Assigning to a binding without mut is a type-checking error.

infer determines the type from the declaration’s initializer. It does not make a binding dynamically typed.

mut value: infer = 1; // i64
value = 2; // OK
// value = 0.5; // type error: cannot assign f64

An integer literal with no contextual type defaults to i64, while a floating-point literal defaults to f64. Explicit type annotations or surrounding expressions can determine a literal’s type instead.

Cerune does not perform implicit numeric conversions. See Types and the primary source’s numeric literal rules.

A mut array binding permits element updates. Arrays have value-copy semantics, so an element update to one copy does not affect another.

The contents of a string value are immutable. A mut string binding can be reassigned to a different string but cannot be used to change part of the string in place.

Braced blocks, including the bodies of if statements and loops, create new scopes.

  • A binding declared in a block is not available outside it.
  • Inner blocks can read outer bindings and reassign them if declared with mut.
  • Inner blocks may declare a distinct binding with the same name as an outer binding (shadowing).
  • Bindings declared by a for initializer are available in its condition, update, and body, but not after the loop.

This page is a summary. Consult the primary specification for exact evaluation-order, assignment, and scoping rules.