Operators
Operators work on values: they do arithmetic, compare things and join conditions together. Most of them look the way you expect; this lesson spends its time on the few places where they do not.
Arithmetic
+ - * / % behave as they do in mathematics, and ** raises to a power. Multiplication and division happen before addition and subtraction, and parentheses change the order.
func main:
a := 17
b := 5
println a + b, a - b, a * b, a / b, a % b
println 2 ** 10
println (2 + 3) * 4, 2 + 3 * 4
end+ also joins strings, and with a string on its left it turns a number into text: "total: " + 5 is "total: 5". The strings lesson has more.
Division and remainder
Dividing two whole numbers gives a whole number: 17 / 5 is 3, and % gives what is left over, 2. That pair is exactly what you need to split minutes into hours, to count how many tables a group needs, or to tell even numbers from odd ones.
func main:
minutes := 135
println minutes / 60, "h", minutes % 60, "min"
guests := 17
per_table := 4
tables := (guests + per_table - 1) / per_table
println "tables needed:", tables
println "empty seats:", tables * per_table - guests
n := 7
println n, "is", n % 2 == 0 ? "even" : "odd"
println -7 / 2, -7 % 2
endTwo details are worth remembering:
- Division cuts toward zero, so
-7 / 2is -3, and the remainder takes the sign of the left side, so-7 % 2is -1. A test such asn % 2 == 1therefore misses negative odd numbers;n % 2 != 0catches them all. %works only on whole numbers; on decimals it is a compile error. Dividing by a literal0is a compile error too, but a zero that turns up at run time is not checked for you and can crash the program, so test the divisor first.
Powers
** raises a number to a power, and its result is always an f64, even for two whole numbers. That lets it handle 2 ** 0.5 and 2 ** -1, but it means you convert with as int when you need a whole number back.
func main:
bacteria := 100 * 2 ** 10
println "after ten doublings:", bacteria
println 2 ** 0.5, 2 ** -1
println 2 ** 3 ** 2, -2 ** 2, (-2) ** 2
cells := (2 ** 10) as int
println cells / 3
end** groups from the right, so 2 ** 3 ** 2 is 2 ** 9, and it binds tighter than a leading minus, so -2 ** 2 is -4, as in mathematics. For the same reason **= works on an f64 variable but not on an int one, because the result would not fit back in.
Comparison
A comparison produces a bool. The operators are ==, !=, <, >, <= and >=, and each has a word form as well: eq, neq, lt, gt, lte and gte. Join conditions with and and or, and invert one with not.
func main:
age := 20
println age > 18, age < 18, age == 20, age != 20
println age eq 20, age neq 30
println age gt 18, age lte 20
println age >= 18 and age <= 60
println age < 10 or age > 15
println not (age > 18)
println "apple" < "banana", "apple" == "apple"
endStrings compare with the same operators. == checks that the text is the same, and < orders by character code, so every capital letter comes before every small one: "Zebra" < "apple" is true.
Comparisons do not chain. 1 < x < 10 compares the bool from 1 < x with 10, and the compiler rejects it (cannot compare 'bool' and 'i32'); write x > 1 and x < 10.
Combining conditions
and and or stop as soon as the answer is known. If the left side of and is false, the right side never runs; if the left side of or is true, the same. That makes it safe to guard a risky check with a simple one, as the first if does here: the division only happens when count is not zero.
func check(name: str): bool:
println " checked", name
ret true
end
func main:
count := 0
total := 50
if count > 0 and total / count > 10:
println "large average"
else:
println "no data yet"
end
println false and check("a")
println true or check("b")
println true and check("c")
logged_in := true
banned := false
println "may post:", logged_in and not banned
endOnly the third call to check ran. not binds tighter than any comparison, so not count == 3 means (not count) == 3 and is an error; write not (count == 3), or simply count != 3. Salam has no &&, || or !; the compiler rejects them and names the word to write instead.
The ternary operator
condition ? a : b gives a when the condition holds and b otherwise. Ternaries can be chained to pick from several ranges, and they read top to bottom: the first condition that holds wins.
func main:
temperature := 32
weather := temperature > 30 ? "hot" : "mild"
println "the weather is", weather
score := 73
grade := score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : "F"
println "grade:", grade
items := 1
println items, items == 1 ? "item" : "items"
endBoth branches must have the same type: n > 3 ? "big" : 0 is an error. A chain of more than three or four cases is usually clearer as a match.
Changing a variable in place
+=, -=, *=, /= and %= apply an operator and store the result back in the variable, which must be mut. += also appends to a string. ++ adds one and -- takes one away.
func main:
mut balance := 100
balance += 50
balance -= 30
balance *= 2
balance /= 3
balance %= 50
println "balance:", balance
mut log := "start"
log += ", saved"
log += ", files: " + 3
println log
mut i := 5
i++
i--
i++
j := i++
println "j:", j, "i:", i
endUsed as a value, i++ gives the old value and then adds one, which is why j is 6 while i ends at 7; ++i adds first. Code is easier to read when ++ and -- stand on their own line.
Bitwise operators
&, |, ^, ~ and the shifts << and >> work on the bits of a whole number.
func main:
a := 0b1100
b := 0b1010
println a & b, a | b, a ^ b
println 1 << 4, 256 >> 2, ~0
endTheir everyday use is a set of yes or no options packed into one number, each option one bit. |= turns an option on, &= ~ turns it off and & tests it. A shift right by 10 divides by 1024.
const READ := 1
const WRITE := 2
const EXEC := 4
func main:
mut perms := READ | WRITE
println "can write:", (perms & WRITE) != 0
perms |= EXEC
perms &= ~WRITE
println "can write:", (perms & WRITE) != 0
println "can run:", (perms & EXEC) != 0
println "bits:", perms
println "5000 bytes in KiB:", 5000 >> 10
endKeep the parentheses in (perms & WRITE) != 0. As in C, & binds more loosely than == and !=, so without them the comparison happens first. The compiler catches this one, since it leaves & with a bool on one side. Bitwise operators accept whole numbers only.
Which operator goes first
From the tightest binding to the loosest. Operators on the same row are worked out from left to right, except **.
| Operators | Example |
|---|---|
as | x / 2 as f64 converts only the 2 |
** (right to left) | 2 ** 3 ** 2 is 512 |
unary -, ~, not | -2 ** 2 is -4 |
*, /, % | 2 + 3 * 4 is 14 |
+, - | 1 << 4 + 1 is 32 |
<<, >> | 1 << 2 < 5 is true |
<, <=, >, >= | |
==, != | |
&, then ^, then | | 6 & 3 | 8 is 10 |
and | |
or | true or true and false is true |
? : | ok ? 1 : 2 + 10 is 1 or 12 |
When you have to stop and think about the order, add parentheses. They cost nothing and the next reader will not have to think either.
Two plus or minus signs in a row, as in a - -b, is an error; write a + b, or put the inner expression in parentheses: a - (-b). Your own types can define what these operators do; see the new types lesson.