Assignment

An assignment stores a new value in a place that already exists — a var, a field, an element, or the target of a writable pointer.

OperatorMeaning
place = valuecopy value into place
place <- valuemove value into place
place += value, -=, *=, /=, %=place = place + value, and so on
place &= value, |=, ^=the bitwise forms
place <<= value, >>=, >>>=the shift forms
place++, ++placeadd one
place--, --placesubtract one
var n: int32 = 10;
n += 5;
n -= 3;
n *= 4;
n /= 5;
n %= 5;

Each line starts from the result of the one before: 15, 12, 48, 9, 4.

The target

The left side must be an assignable place: a variable, a field (p.x), a tuple element (pair.0), an indexed element (values[i]), or a dereferenced pointer (*ptr). And it must be mutable — a var, or something reached through one, a &var reference or a *var pointer:

var p = Point { x: 1, y: 2 };
p.x += 10;
p.y++;
var values: int32[3] = [1, 2, 3];
values[1] *= 7;
TargetError
a letcannot modify immutable variable 'fixed'
a constantcannot modify constant 'Limit'
a value that is not a placeoperator '=' requires an assignable target, but its left operand has type 'int'
through a read-only pointercannot modify data through read-only pointer '*int'

The error for a let comes with help: declare 'fixed' with 'var' to make it mutable.

Type rules

The value must have the place's type, or widen to it without loss — an integer of the same signedness and a smaller width, as in a binding. A compound assignment follows the rules of its operator, and its result must still fit the place:

var total: int32 = 0;
let big: int64 = 5;
total += big;
error: operator '+=' produces 'int64', which cannot be stored in target type 'int32'

total += big as int32 converts first. Mixing kinds is refused just as the plain operator would refuse it: on an int32, score += 1.5 is error: operator '+=' cannot combine left operand 'int32' with right operand 'float64', and on a uint8, u += 300 is error: integer literal is out of range for type 'uint8'.

An assignment produces no value

An assignment is a complete action, not a value. The grammar reads a = b = c from the right, as a = (b = c), but b = c has nothing to give a:

first = second = 7;
error: an assignment produces no value and cannot be chained
  help: assign each target in its own statement

For the same reason = cannot stand in for == in a condition: if a = 5 { … } is refused, because the condition is not a bool.

++ and --

++ adds one and -- subtracts one. They apply to any integer or floating-point place, and need it to be mutable like any other assignment: on a let, k++ is error: cannot modify immutable variable 'k'.

On a line of its own, the prefix and postfix forms are the same. Used as a value, they differ in what they hand back:

FormValue of the expressionAfterwards
count++the old valueone higher
++countthe new valueone higher
count--the old valueone lower
--countthe new valueone lower
var count: int32 = 1;
let before = count++;
let after = ++count;
PrintLine("{} {} {}", before, after, count);

This prints 1 3 3. Code that leans on the difference is easy to misread; most Rux code keeps ++ and -- on lines of their own.

Rux has no unary +, so a =+ 3 does not parse; and a =- 3 is a = -3, a valid assignment of minus three. The operator always comes before the =.

Moving with <-

place <- value moves instead of copying: the value is handed over and its source may not be read again. The place's old value is destroyed as it is replaced, just as with =:

let first = Buffer { size: 4 };
var second = Buffer { size: 0 };
second <- first;

Reading first afterwards is error: value 'first' is used after it was moved. A type that cannot be copied is assigned from a named source only with <-. When copy and move apply, and what replacing a value destroys, are covered in Copy and move.

See also