Variables
Declaration Keywords
Section titled “Declaration Keywords”Ard uses two keywords for variable declaration:
letfor immutable bindingsmutfor mutable bindings
Type Inference
Section titled “Type Inference”Variable types can be inferred from their initial values:
let count = 42let pi = 3.14let greeting = "Hello"let active = trueExplicit Type Annotations
Section titled “Explicit Type Annotations”Types can be optionally be declared:
let name: Str = "Bob"let temperature: Float = 98.6let items: [Int] = [1, 2, 3]let map: [Str:Int] = ["a": 1, "b": 2]Immutability with let
Section titled “Immutability with let”An immutable binding is read-only meaning it can neither be reassigned or mutated:
let x = 10x = 20 // Error: cannot reassign immutable variable
let numbers = [1, 2, 3]numbers.push(4) // Error: cannot mutate immutable variableMutability with mut
Section titled “Mutability with mut”Variables declared with mut can be modified:
mut counter = 0counter = 5 // OKcounter =+ 1 // OK, increment by 1Increment and Decrement
Section titled “Increment and Decrement”Ard uses a unique syntax for compound assignment operators, placing the = first for left-to-right readability:
mut value = 10
value =+ 5 // Equivalent to value = value + 5value =- 2 // Equivalent to value = value - 2There are no ++ or -- operators in Ard and only increment (=+) and decrement (=-) are supported.
Scoping Rules
Section titled “Scoping Rules”Variables follow standard block scoping rules:
let outer = "global"
if some_condition { let inner = "local" // Both 'outer' and 'inner' are accessible here}
// Only 'outer' is accessible here// 'inner' is out of scopeMutable References
Section titled “Mutable References”A mut binding creates mutable local storage. In type positions, mut T means mutable reference to a T.
A function parameter marked mut receives mutable access to caller-owned storage, so the caller must pass an addressable mutable value. There is no extra mut marker at the call site:
struct Person { name: Str, age: Int }
fn update_person(mut person: Person) { person.age = 99 // Mutates the caller's value}
mut alice = Person { name: "Alice", age: 30 }update_person(alice)// alice.age is now 99Passing an immutable value to a mutable parameter is a compile-time error:
let bob = Person { name: "Bob", age: 30 }update_person(bob) // Error: expected a mutable PersonMutable references may alias. If two mut T references point at the same mutable storage, mutations through either reference are visible through the other.
Mutable Reference Fields
Section titled “Mutable Reference Fields”Struct fields can hold mutable references:
struct Context { tree: mut ViewTree,}
let ctx = Context{tree: tree}ctx.tree.add_child(child)The ctx binding is immutable, but ctx.tree is mutable access to the referenced ViewTree. Field assignment writes through the reference; it does not rebind the field slot.
mut T is also a representation boundary for recursive types, so it can be used to model linked structures and retained object graphs that require identity.
Shadowing
Section titled “Shadowing”Redeclaring a variable with the same name in the same scope is allowed. This acts as a wipe of the binding and is only valid if their usages are consisten.
let x = 5let x = x + 1 // Creates new variable, x is now 6let x: Str = "hello" // Creates new variable with different typex.size() // x is now a string and can only be used as a string