Assignment expressions
Assignment operators:
=and<-(left arrow) are the same assignment in meaning: evaluate the right-hand side and store it in the target (variable, index, or member, where the language allows). The two tokens differ only in syntax—use whichever you prefer for readability or style; there is no separate “mode” in behavior between them.
Compound assignment
+=, -=, *= and /= combine an operation with the store: x += e means x = x + e.
var x: int = 10
x += 5
print(x, "\n")
x -= 3
print(x, "\n")
x *= 4
print(x, "\n")
var d: deci = 10.0
d /= 4.0
print(d, "\n")
Since + concatenates strings, += appends:
var s: str = "Hello"
s += " World"
print(s, "\n")
They work on any assignable target — a variable, an array element, an object or class member, an inout parameter:
var arr: int[3] = [1, 2, 3]
arr[1] += 10
print(arr, "\n")
type T = { v: int }
var o: T = { v: 5 }
o.v *= 3
print(o.v, "\n")
The target is evaluated once. In arr[next()] += 10 the index expression runs a single time — writing it out as arr[next()] = arr[next()] + 10 would call next() twice.
The usual home for these is a loop accumulator:
var total: int = 0
var i: int = 0
while i < 5 {
total += i
i += 1
}
print(total, "\n")
var joined: str = ""
for (var k: int = 0 to 3 : 1) {
joined += String(k)
}
print(joined, "\n")
Like =, a compound assignment is itself a value, so it composes with the chaining described below:
var p: int = 2
var q: int = 0
q = p *= 5
print(q, "\n")
print(p, "\n")
Both print 10.
When a compound assignment is rejected
x op= e is valid exactly when x = x op e is. Two consequences worth knowing:
/=on twointvalues is an error, because/produces adeci(1 / 2is0.5) and that does not fit back into anint. Use adecitarget, or//=-style floor division written out asx = x // 2.- Narrow integer targets follow the same promotion rules as ordinary arithmetic:
int8 + int8produces anint, soi += jonint8is rejected just asi = i + jis.
A compound assignment also takes exactly one target — a, b += 1, 2 is an error. Multi-target assignment is available with plain =.
An assignment is also a value
An assignment produces a value: the target it just stored into. So assignments chain, and they can appear anywhere a value is expected.
Chaining is right-associative — x = y = 3 stores 3 into y first, then stores y’s new value into x:
var x: int = 0
var y: int = 0
x = y = 3
print(x, "\n")
print(y, "\n")
Both print 3. The same works with <- (x <- y <- 3), and the two can be mixed in one chain.
Because it is a value, an assignment also works inside a larger expression — a declaration’s initializer, a call argument, a condition, or an operand:
var y: int = 0
var total: int = 1 + (y = 2)
print(total, "\n")
print(y, "\n")
total is 3 and y is 2.
Objects and arrays chain too, and follow the same replacement rules as any other assignment:
type Person = {
name: str,
age: int,
}
var p1: Person = { name: "", age: 0 }
var p2: Person = { name: "", age: 0 }
p1 = p2 = { name: "Alice", age: 30 }
print(p1, "\n")
print(p2, "\n")
Multi-target assignments
When an assignment has several targets, its value is the first (leftmost) one:
var a: int = 0
var b: int = 0
var x: int = 0
x = (a, b = 1, 2)
print(x, "\n")
print(a, "\n")
print(b, "\n")
a becomes 1 and b becomes 2, and x takes the first target’s value — 1.
Watch out: assignment in a condition
Since an assignment is a value and a number is truthy, if (x = 3) is valid — it assigns 3 to x and then tests it. That is an assignment, not the comparison x == 3:
var x: int = 0
if (x = 3) {
print("this runs\n")
}
print(x, "\n")
If you meant to compare, write ==.
Omitted object fields in a literal are filled from type defaults (e.g. 0 for int, empty str, etc.), not from whatever was in the variable before. To change only one field, assign to that field:
type Person = {
name: str,
age: int,
}
var person: Person = { name: "Alice", age: 30 }
person.name = "Bob"
print(person)
That keeps age: 30 and updates name. By contrast, replacing the whole value with a partial literal overwrites the whole value with a new object where missing fields are defaults:
type Person = {
name: str,
age: int,
}
var person: Person = { name: "Alice", age: 30 }
person = { name: "Bob" }
print(person)
You get age: 0 (default for int) and name: "Bob" — not age: 30. The same happens if you write person <- { name: "Bob" }: the operator does not “merge” old field values. To keep age: 30, use a field assignment (first example) or a full new literal, not a partial one.
Fixed-size array example
For Person[2], assigning from an array literal with fewer elements than the fixed size still produces a value of the array type: remaining slots are set from defaults, not the previous array’s elements.
type Person = {
name: str,
age: int,
}
var people: Person[2] = [
{ name: "Alice", age: 30 },
{ name: "Bob", age: 30 }
]
people = [{ name: "Charlie", age: 30 }]
print(people)
Typical result: the first entry matches the literal; the second is default-initialized (e.g. empty name, age 0), not the old "Bob" row. You could write people <- [...] here with the same effect.
Notes
x <- 1andx = 1,person <- { name: "Bob" }andperson = { name: "Bob" }are interchangeable; pick=or<-for taste.- For partial object or array literals, the same defaulting rules apply: if you need old data preserved, do not rely on a partial literal; update fields explicitly or build a new full value.