Operators · Lesson 2.4

Assignment

Source
Update a variable in place with += and friends, and step it by one with ++ and --.
You'll need: Mutable, Arithmetic

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 formMeansscore goes from 10 to
score += 5score = score + 515
score -= 3score = score - 37
score *= 4score = score * 440
score /= 5score = score / 52
score %= 5score = score % 50

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.

Src/Main.rux
// 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

Updating a 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.
Mixing types.
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.
Writing =- 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

  1. Start a var balance: int32 = 100;, apply -= 30, *= 2 and %= 7, and predict each step before you run.
  2. Halve a var temperature: float64 = 37.0; with /= and print it.
  3. Print count++ and ++count directly inside a PrintLine, and check the results against the diagram.
  4. Change var score to let score and read every error the compiler reports.

Learn more