Operators and expressions
Most of Go's operators behave the way you would expect from any C-family language. There are a couple of small but important differences, especially around integer division, increment statements, and how booleans combine.
Arithmetic
The five arithmetic operators are +, -, *, /, and %. The first four work on numeric types; % is for integers only.
a, b := 10, 3
fmt.Println(a + b) // 13
fmt.Println(a - b) // 7
fmt.Println(a * b) // 30
fmt.Println(a / b) // 3 (integer division, not 3.33)
fmt.Println(a % b) // 1 (remainder)
The one to watch is /. When both operands are integers, it performs integer division and throws away the fractional part. In the example, 10 / 3 prints 3, not 3.33. If you need a fractional result, at least one operand has to be a float. The Type conversions lesson you just finished is how you get there:
fmt.Println(float64(a) / float64(b)) // 3.3333333333333335
The remainder operator % returns what is left over after integer division. 10 % 3 is 1. It works on integers only; the math package has math.Mod for floats.
Comparison
The comparison operators are the familiar six: ==, !=, <, >, <=, >=. Each returns a bool.
fmt.Println(a > b) // true
fmt.Println(a == b) // false
fmt.Println(a >= 10) // true
Comparison only works between values of the same type. Untyped literals like 1 and 1.0 adapt to a common type automatically, so 1 == 1.0 compiles and is true. But once the types are fixed, Go refuses to mix them:
var i int = 1
var f float64 = 1.0
fmt.Println(i == f) // error: mismatched types int and float64
fmt.Println(float64(i) == f) // fine, true
When the compiler objects, convert one side so both operands share a type.
Logical operators
The three logical operators are && (and), || (or), and ! (not). They take booleans and return a boolean.
fmt.Println(a > b && a%b == 1) // true
fmt.Println(a < b || b > 0) // true
fmt.Println(!(a < b)) // true
&& and || short-circuit: if the left side of && is false, Go does not evaluate the right side because the answer is already known. Same for || when the left side is true. This matters when the right side has a side effect or would panic on invalid input:
x := 0
safe := x != 0 && 10/x > 1 // 10/x is never evaluated because x != 0 is false
fmt.Println(safe) // false, and no divide-by-zero panic
Without short-circuit, 10/x with x == 0 would crash the program. The x != 0 check guards it: because && stops as soon as the left side is false, the divide never runs. This pattern (check a condition first, then dereference or divide) is how Go handles the role that nullable-aware operators play in some other languages.
Increment and decrement are statements
x++ and x-- exist, but they are statements, not expressions. You can write x++ on its own line, but you cannot use it inside another expression:
x := 1
x++ // fine, x is now 2
y := x++ // compile error
fmt.Println(x++) // compile error
Go made this change on purpose to avoid the small but persistent bugs that come from mixing an increment with another expression. If you want the new value of x elsewhere, do the increment on its own line, then use x.
Integer division truncates toward zero, not toward minus-infinity. 7 / 2 is 3, but -7 / 2 is -3, not -4. Try it: add fmt.Println(7 / 2) and fmt.Println(-7 / 2) and compare. The same rule applies to %: the sign of the result follows the dividend, so -7 % 2 is -1. Languages that round toward minus-infinity (Python, Ruby) would give you -4 and 1 instead, which is the single most common surprise when porting arithmetic between them and Go.
Extend the starter with two more fmt.Println lines:
fmt.Println(a > b)should printtrue.fmt.Println(a > b && a%b == 1)should also printtrue, since10 > 3and10 % 3 == 1are both true.
13 7 30 3 1 true true