Common Operators

Operator operations are the operations using all kinds of operators. This article will introduce common operators in Go. More operators will be introduced in other articles later.

About Some Descriptions In Operator Explanations

This article will only introduce arithmetic operators, bitwise operators, comparison operators, boolean operators and string concatenation operator. These operators are either binary operators or unary operators. A binary operator operation takes two operands and a unary operator operation takes only one operand.

All the operator operations introduced in this articles each returns one result.

This article doesn't pursue the accuracy of some descriptions. For example, when it says that a binary operator requires the types of its two operands must be the same, what it means is Slimilarly, when it says an operator, either a binary operator or a unary operator, requires the type of one of its operands must be of a certain type, what it means is

Arithmetic Operators

Go supports five basic binary arithmetic operators:

Operator Name Requirements For The Two Operands
+ addition The two operands must be both values of the same basic numeric type.
- subtraction
* multiplication
/ division
% remainder The two operands must be both values of the same basic integer type.

The five operators are also often called sum, difference, product, quotient and modulo operators. Go 101 will not explain how these operator operations work in details.

Go supports six bitwise binary arithmetic operators:

Operator Name Requirements For The Two Operands And Mechanism Explanations
& bitwise and

The two operands must be both values of the same integer type.

Mechanism explanations (a value with the subscript 2 is the binary literal form of the value):
  • 11002 & 10102 results 10002
  • 11002 | 10102 results 11102
  • 11002 ^ 10102 results 01102
  • 11002 &^ 10102 results 01002
| bitwise or
^ bitwise xor
&^ bitwise clear
<< bitwise left shift

The left operand must be an integer and the right operand must be an unsigned integer (or an untyped integer constant which is representable as an uint value).

Mechanism explanations:
  • 11002 << 3 results 11000002
  • 11002 >> 3 results 12
>> bitwise right shift

Go also supports three unary arithmetic operators:

Operator Name Explanations
+ positive +n is equivalent to 0 + n.
- negative -n is equivalent to 0 - n.
^ bitwise complement
(bitwise not)
^n is equivalent to m ^ n, where m is a value all of which bits are 1. For example, if the type of n is int8, then m is -1, and if the type of n is uint8, then m is 0xFF.
Note,

Example:
func main() {
	var (
		a, b float32 = 12.0, 3.14
		c, d int16   = 15, -6
		e    uint8   = 7
	)

	// The ones compile okay.
	_ = 12 + 'A' // two untyped operands (both are numeric)
	_ = 12 - a   // one untyped operand and one typed operand
	_ = a * b    // two typed operands
	_ = c % d
	_, _ = c + int16(e), uint8(c) + e
	_, _, _, _ = a / b, c / d, -100 / -9, 1.23 / 1.2
	_, _, _, _ = c | d, c & d, c ^ d, c &^ d
	_, _, _, _ = d << e, 123 >> e, e >> 3, 0xF << 0
	_, _, _, _ = -b, +c, ^e, ^-1

	// The following ones fail to compile.
	_ = a % b   // error: a and b are not integers
	_ = a | b   // error: a and b are not integers
	_ = c + e   // error: type mismathing
	_ = b >> 5  // error: b is not an integer
	_ = c >> -5 // error: -5 is not representable as uint
	_ = e << c  // error: c is not an unsigned integer
}

About The Results Of Arithmatic Operator Operations

The result of a binary arithmetic operator operation (except bitwise shift operations) The rules for the result of a bitwise shift operator operation is a little complex. Firstly, the result value is always an interger value. Whether it is typed or untype depends on specific scenarios. Example:
func main() {
	const X, Y, Z = 2, 'A', 3i // three untyped values. 
	                           // Default types: int, rune, complex64.
	
	var a, b int = X, Y // two typed values.

	d := X + Y // the type of d is the default type of Y: rune (int32).
	e := Y - a // the type of e is the type of a: int.
	f := a * b // the type of f is the types of a and b: int.
	g := Z * Y // the type of g is the default type of Z: complex64.

	println(X, Y, Z)    // 2 65 (+0.000000e+000+3.000000e+000i)
	println(d, e, f, g) // 67 63 130 (+0.000000e+000+1.950000e+002i)
}

Another example (bitwise shift operations):
const N = 2
const A = 3.0 << N       // A == 6, its default type is int.
const B = int8(3.0) << N // B == 6, its default type is int8.

var m = uint(32)
// The following three lines are equivalent to each other.
var x int64 = 1 << m  // the type of 1 is deduced as int64, not int.
var y = int64(1 << m) // the type of 1 is deduced as int64, not int.
var z = int64(1) << m

// The following lines fail to compile.
/*
var _ = 3.0 << m // error: shift of type float64
const _ = 1 << B // error: right the operand must be unsigned.
*/

The rules for bitwise shift operator operations are to avoid some operations returning different results on different architectures. For exampe the bitwise operation at line 8 will return different results between 32-bit OSes and 64-bit OSes if the operand 1 is deduced as int instead of int64, which may produce some bugs hard to detect.

About Overflows

Overflows are not allowed for typed constant values but are allowed for non-constant and untyped constant values, either the values are intermediate or final results. Overflows will be truncated (or wrapped around) for non-constant values, but overflows (for default types) on untyped constant value will not be truncated (or wrapped around).

Example:
var a, b uint8 = 255, 1
var c = a + b  // okay: higher overflowed bits are truncated. c == 0
var d = a << b // okay: higher overflowed bits are truncated. d == 254
const X = 0x1FFFFFFFF * 0x1FFFFFFFF // okay, though X overflows int.
const R = 'a' + 0x7FFFFFFF          // okay, though R overflows rune.

// These lines fail to compile.
var e = X                // error: untyped constant X overflows int.
var h = R                // error: constant 2147483744 overflows rune.
const Y = 128 - int8(1)  // error: 128 overflows int8
const Z = uint8(255) + 1 // error: the result 256 overflow uint8.

About Divisions

The two operands in a division operator opeartion can be both of any basic numeric type. If their types is an integer type, assume the two operands are x and y, the integer quotient q (= x / y) and remainder r (= x % y) satisfy x == q*y + r, where |r| < |y|. If r is not zero, its sign is the same as x (the dividend). The result of x / y is truncated towards zero.

If the divisor y is a constant, it must not be zero. If the divisor is zero at run time and it is an integer, a run-time panic occurs. Panics is like exceptions in some other languages. We can learn more about panics in this article.

Example:
println( 5/3,   5%3)  // 1 2
println( 5/-3,  5%-3) // -1 2
println(-5/3,  -5%3)  // -1 -2
println(-5/-3, -5%-3) // 1 -2

println(5.0 / 3.0)     // 1.666667
println((1-1i)/(1+1i)) // -1i

var a, b = 1.0, 0.0
println(a/b, b/b) // +Inf NaN

_ = int(a)/int(b) // compile okay but panic at run time.

// The following two lines fail to compile.
println(1.0/0.0) // error: division by zero
println(0.0/0.0) // error: division by zero

Using op= For Binary Arithmatic Operators

For a binary arithmetic operator op, x = x op y can be shortened to x op= y. In the short form, x will be only evaluated once.

Example:
var a, b int8 = 3, 5
a += b
println(a) // 8
a *= a
println(a) // 64
a /= b
println(a) // 12
a %= b
println(a) // 2
b <<= uint(a)
println(b) // 20

The Increment ++ And Decrement -- Operators

Like many other popular languages, Go also supports the increment ++ and decrement -- operators. However, operations using the two operators don't return any results, so such operations can be used as expressions. The only operand involved in such an operation must be a numeric value, the numeric value must not be a constant, and the ++ or -- operator must follow the operand.

Example:
package main

func main() {
	a, b, c := 12, 1.2, 1+2i
	a++ // okay
	b-- // okay
	c++ // oaky
	
	// The following three lines fail to compile.
	/*
	_ = a++
	_ = b--
	_ = c++
	++a
	--b
	++c
	*/
}

String Concatenation Operator

As above has mentioned, the addition operator can also be used as string concatenation.
Operator Name Requirements For The Two Operands
+ string concatenation The two operands must be both values of the same string type.

The op= form also applies for the string concatenation operator.

Example:
println("Go" + "lang") // Golang
var a = "Go"
a += "lang"
println(a) // Golang

If one of the two operands of a string concatenation operation is a typed string, then the type of the result string is the same as the type of the typed string. If both the of the two oprands are untyped (constant) strings, the result is also an untyped string value.

Boolean Operators

Go supports two boolean binary operators and one boolean unary operator:

Operator Name Requirements For Operand(s) And Mechanism Explanations
&& boolean and (binary) The two operands must be both values of the same boolean type.
|| boolean or (binary)
! boolean not (unary) The type of the only operand must be a boolean type.

We can use the != operator introduced above as the boolean xor operator.

Mechanism explanations:
// x      y     x && y     x || y    !x     !y
true    true    true       true     false  false
true    false   false      true     false  true
false   true    false      true     true   false
false   false   false      false    true   true

If one of the two operands is a typed boolean, then the type of the result boolean is the same as the type of the typed boolean. If both the of the two oprands are untyped booleans, the result is also an untyped boolean value.

Comparison Operators

Go supports six comparison binary operators:

Operator Name Requirements For The Two Operands
== equal to If at least one operand is typed, then one operand must be able to be implicitly converted to the type of the other operand. Otherwise, the two operands must be both untyped booleans, both untyped strings or both untyped numeric values.
!= not equal to
< less than The two operands must be both values of the same integer type, floating-point type or string type.
<= less than or equal to
>= larger than
>= larger than or equal to

The type of the result of any comparison operation is always an untype boolean value. If both of the two operands of a comparison operation are constant, the result is also a constant (bollean) value.

Later, if we say two values are comparable, we mean they can be compared with the == and != operators. We will learn that values of which types are not comparable later. Values of basic types are all comparable.

Please note that, not all real numbers can be accurately represented in memory, so comparing two floating-point (or complex) values may be not reliable. We should check whether or not the absolution of the difference of two floating-point values is smaller than a small threshold to judge whether or not the two floating-point values are equal.

Operator Precedences

The operator precedences may be also some different to other languages. Here are the operator precedences in Go. Top ones have higher precedences. The precedences of operators in the same line are same. Like many other languages, () can be used to promote precedences.

*   /   %   <<  >>  &   &^
+   -   |   ^
==  !=  <   <=  >   >=
&&
||

One abvious difference to some other popular languages is that the precedences of << and >> are higher than + and - in Go.

Constant Expressions

If every operand in an operator operation introduced above are constants, then the operator operation is called a constant expression. All constant expressions are evaluated at compile time. Each constant expression is also viewed as a constant.

A constant expression, in fact any expression, can be used as an operand (as a sub-expression) of another operator operation.

The following declared variable will be initialized as 2.2 instead of 2.7. The reason is the division operation has a higher precedence than the addition operation, and in the division operation, both 3 and 2 are viewed as integers.
var x = 1.2 + 3/2

The two named constants declared in the following program are not equal. In the first declaration, both 3 and 2 are viewed as integers, however, they are both viewed as floating-point numbers in the second declaration.
package main

const x = 3/2*0.1
const y = 0.1*3/2

func main() {
	println(x, y) // +1.000000e-001 +1.500000e-001
}

More Operators

Same as C/C++, there are two pointer related operators, * and &. Yes the same operator symbols as the multiplication and bitwise-and operators. & is used to take the address of an addressable value, and * is used to deference a pointer value. Unlike C/C++, in Go, values of pointer types don't support arithmetic operations. For more details, please read pointers in Go later.

There are some other operators in Go. They will be introduced and explained in other Go 101 articles.

Welcome to improve Go 101 articles by submitting corrections for all kinds of mistakes, such as typos, grammar errors, wording inaccuracies description flaws and code bugs.