Assignment
A running total, a countdown, a score: many variables change by building on their own current value. Writing score = score + 5 works, but it names score twice and makes the reader check that both names really are the same. Rux, like most languages, has a shorter form for "change this variable by this much".
Compound assignment
score += 5 means exactly score = score + 5. Every arithmetic operator has a compound form:
var score: int32 = 10;
PrintLine("start {}", score);
score += 5;
PrintLine("+= 5 {}", score);
score -= 3;
PrintLine("-= 3 {}", score);
| Short form | Means | score goes from 10 to |
|---|---|---|
score += 5 | score = score + 5 | 15 |
score -= 3 | score = score - 3 | 7 |
score *= 4 | score = score * 4 | 40 |
score /= 5 | score = score / 5 | 2 |
score %= 5 | score = score % 5 | 0 |
The program applies them one after another, so each line starts from the result of the one before: 10, 15, 12, 48, 9, 4. The bitwise operators of a later lesson have compound forms too.
Stepping by one
Adding or taking away exactly one is so common that it is shorter still. score++ adds one, score-- takes one away:
score++;
PrintLine("++ {}", score);
score--;
score--;
PrintLine("-- twice {}", score);
These are the steps loops take on every pass, and you will see them throughout Part 3.
Floats too
Compound assignment follows the rules of the operator inside it. On a float64, /= is float division, so it keeps the fraction, and ++ adds 1.0:
var price: float64 = 10.0;
price /= 4.0;
price++;
PrintLine("price {}", price);
10.0 / 4.0 is 2.5, plus one is 3.5. The two sides must still agree on type: score += 1.5 on an int32 is refused just as score + 1.5 would be.
Before or after
On a line of its own, count++ and ++count do the same thing. Inside a larger expression, where you write the ++ decides which value the expression hands back:
var count: int32 = 1;
let before = count++;
PrintLine("count++ gave {}, count is now {}", before, count);
let after = ++count;
PrintLine("++count gave {}, count is now {}", after, count);
flowchart LR
post["count++"] --> p1["hands back the old value"] --> p2["then count is one higher"]
pre["++count"] --> q1["count is one higher"] --> q2["hands back the new value"]Either way the variable ends up one higher. Code that depends on the difference is easy to misread, so most Rux code keeps ++ and -- on lines of their own.
They all need a var
Every one of these operators writes to the variable, so the variable must be declared with var, as in Mutable. On a let binding the compiler refuses them, just as it refuses a plain =.
The program
The whole lesson is one package in the Examples repository. Its comments explain every step.
// Changing a variable based on its own value is so common that it has a short
// form. `score += 5` means `score = score + 5`, and every arithmetic operator
// has one: `+=`, `-=`, `*=`, `/=` and `%=` (the bitwise operators of a later
// lesson have them too). Stepping by exactly one is shorter still: `score++`
// adds one and `score--` takes one away.
//
// All of these write to the variable, so they need a `var`. On a `let` binding
// the compiler refuses them, just as it refuses a plain `=`.
import Io::PrintLine;
func Main() -> int {
var score: int32 = 10;
PrintLine("start {}", score);
score += 5;
PrintLine("+= 5 {}", score);
score -= 3;
PrintLine("-= 3 {}", score);
score *= 4;
PrintLine("*= 4 {}", score);
score /= 5;
PrintLine("/= 5 {}", score);
score %= 5;
PrintLine("%= 5 {}", score);
score++;
PrintLine("++ {}", score);
score--;
score--;
PrintLine("-- twice {}", score);
// They work on floats too. `/=` follows the type's own division, so here it
// keeps the fraction.
var price: float64 = 10.0;
price /= 4.0;
price++;
PrintLine("price {}", price);
// `++` can also stand inside a larger expression, and then where you write
// it matters. After the variable, `count++` steps it and hands back the
// value from before. Before the variable, `++count` steps it and hands back
// the new value. Either way the variable ends up one higher.
var count: int32 = 1;
let before = count++;
PrintLine("count++ gave {}, count is now {}", before, count);
let after = ++count;
PrintLine("++count gave {}, count is now {}", after, count);
return 0;
}
Run it
cd Examples/Operators/Assignment
rux run
start 10
+= 5 15
-= 3 12
*= 4 48
/= 5 9
%= 5 4
++ 5
-- twice 3
price 3.5
count++ gave 1, count is now 2
++count gave 3, count is now 3
Common mistakes
let.let score: int32 = 10; followed by score += 5; or score++; fails with error: cannot modify immutable variable 'score', and the compiler's help says to declare score with var.score += 1.5 on an int32 fails with error: operator '+=' cannot combine left operand 'int32' with right operand 'float64'. Compound assignment follows the same type rules as the operator inside it.=- instead of -=.score =- 3; is not a typo the compiler can catch: it reads as score = -3, which is valid, and score silently becomes -3. (=+ is refused, because Rux has no unary +.) The operator always goes before the =.Try it yourself
- Start a
var balance: int32 = 100;, apply-= 30,*= 2and%= 7, and predict each step before you run. - Halve a
var temperature: float64 = 37.0;with/=and print it. - Print
count++and++countdirectly inside aPrintLine, and check the results against the diagram. - Change
var scoretolet scoreand read every error the compiler reports.
Learn more
- Assignment operators in the Rux Reference
varand mutability- Precedence — assignment is the loosest operator of all