Variables
Declaration keywords
Section titled “Declaration keywords”Ard uses two declaration keywords:
letcreates a binding whose slot cannot be reassigned.mutcreates a binding whose slot can be reassigned.
The keyword controls the binding slot, not whether the stored value has mutable interior access.
let name = "Ada"// name = "Grace" // Error: the binding slot is immutable
mut count = 1count = 2count =+ 1Type inference and annotations
Section titled “Type inference and annotations”Types are normally inferred, but annotations are available:
let name: Str = "Bob"let temperature: Float64 = 98.6let items: [Int] = [1, 2, 3]let labels: [Str: Int] = ["a": 1, "b": 2]A reference value keeps its reference type during inference:
struct User { name: Str }
let user = User{name: "Ada"}let reference = mut user // inferred as mut Userlet alias = reference // also mut User; copies the reference handleBinding mutability and reference values
Section titled “Binding mutability and reference values”Binding mutability and mutable-reference values are independent:
| Declaration | Slot can be reassigned | Stored value is a reference | Interior mutation |
|---|---|---|---|
let user = User{name: "Ada"} | no | no | no |
mut user = User{name: "Ada"} | yes | no | no |
let user = mut User{name: "Ada"} | no | yes | yes |
mut user = mut User{name: "Ada"} | yes | yes | yes |
A mutable ordinary binding permits whole-slot replacement, but not interior mutation:
struct User { name: Str }
mut user = User{name: "Ada"}user = User{name: "Grace"} // OK: replaces the binding slot// user.name = "Lin" // Error: user stores an ordinary User valueCreate an actual reference to mutate a value’s interior. The source storage may be declared with either let or mut:
let user = User{name: "Ada"}let reference = mut userreference.name = "Grace"A let reference cannot be rebound, but it can mutate its pointee. A mut reference binding can also replace its own stored handle:
let first = User{name: "First"}let second = User{name: "Second"}mut current = mut firstlet alias = current
current = mut second // rebinds only currentalias.name = "One" // still mutates firstcurrent.name = "Two" // mutates secondExplicit reference destinations
Section titled “Explicit reference destinations”mut T in a type position means “a mutable reference to T.” Such a destination requires an actual reference value; a writable ordinary binding is not borrowed implicitly.
fn rename(user: mut User, name: Str) { user.name = name}
let user = User{name: "Ada"}rename(mut user, "Grace")
let reference = mut userrename(reference, "Lin")mut expression has three useful behaviors:
- borrowing addressable local, field, or module storage;
- copying an existing reference handle (
mut referenceis idempotent); - creating stable fresh storage for a value expression such as a literal or call result.
Copy-producing accessors still produce fresh storage rather than a reference into the container.
Explicit shallow values with deref
Section titled “Explicit shallow values with deref”References remain references during ordinary value flow. Use deref when a destination needs the current T value:
let user = User{name: "Ada"}let reference = mut userlet snapshot: User = deref referencederef removes exactly one outer reference layer and evaluates its operand once. The copy is shallow:
- structs, fixed arrays, and primitive values copy their current value;
- reference-valued fields keep copied reference handles;
- lists initially share their existing backing storage, although later growth may detach one descriptor;
- maps continue sharing map contents;
- channels and foreign handles retain their intrinsic sharing behavior.
deref is not a deep-copy operation, and Ard does not provide one. Programs that need an independent deep copy construct it explicitly.
References compare by pointer identity. Compare referent values explicitly when their value types support equality:
let same_place = reference == mut user
let count = 1let count_reference = mut countlet count_snapshot = deref count_referencelet same_value = deref count_reference == count_snapshotReference-valued fields
Section titled “Reference-valued fields”Struct fields can store references:
struct Tree { value: Int }struct Context { tree: mut Tree }
let tree = Tree{value: 1}let context = mut Context{tree: mut tree}context.tree.value = 2
let other = Tree{value: 3}context.tree = mut other // rebinds the field's reference slotThe containing value must itself be reached through a reference to rebind a reference-valued field. Reading or mutating the referenced tree does not require the field’s binding slot to be reassignable.
Shadowing
Section titled “Shadowing”Redeclaring a name in the same scope creates a new binding:
let x = 5let x = x + 1let x: Str = "hello"x.size()