현재 3권의 신간들인 Go Optimizations 101, Go Details & Tips 101Go Generics 101이 출간되어 있습니다. Leanpub 서점에서 번들을 모두 구입하시는 방법이 비용 대비 효율이 가장 좋습니다.

Go에 대한 많은 정보들과 Go 101 책들의 최신 소식을 얻으시려면 Go 101 트위터 계정인 @Go100and1을 팔로잉 해주세요.

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 article 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:

Similarly, when it says an operator, either a binary operator or a unary operator, requires the type of one of its operands to be of a certain type, what it means is:

Constant Expressions

Before introducing all kinds of operators, we should know what are constant expressions and a fact in the evaluations of constant expressions. Expressions will get explained in a later article expressions and statements. At present, we just should know that most of the operations mentioned the current article are expressions.

If all the operands involved in an expression are constants, then this expression is called a constant expression. All constant expressions are evaluated at compile time. The evaluation result of a constant expression is still a constant.

Only if one operand in an expression is not a constant, the expression is called a non-constant expression.

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, respectively. Go 101 will not explain how these operator operations work in detail.

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 in 10002
  • 11002 | 10102 results in 11102
  • 11002 ^ 10102 results in 01102
  • 11002 &^ 10102 results in 01002

|

bitwise or

^

bitwise xor

&^

bitwise clear


<<


bitwise left shift

The left operand must be an integer and the right operand must be also an integer (if it is a constant, then it must be non-negative), their types are not required to be identical. (Note, before Go 1.13, the right operand must be an unsigned integer or an untyped integer constant which is representable as an uint value.)

A negative right operand (must be a non-constant) will cause a panic at run time.

Mechanism explanations:
  • 11002 << 3 results in 11000002
  • 11002 >> 3 results in 12

Note: in a bitwise-right-shift operation, all the freed-up bits at left are filled with the sign bit (the highest bit) of the left operand. For example. if the left operand is an int8 value -128, or 100000002 in the binary literal form, then 100000002 >> 2 results 111000002, a.k.a., -32.



>>


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 numeric untyped operands
	_ = 12 - a   // one untyped 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 mismatching
	_ = b >> 5  // error: b is not an integer
	_ = c >> -5 // error: -5 is not representable as uint

	_ = e << uint(c) // compiles ok
	_ = e << c       // only compiles ok since Go 1.13
	_ = e << -c      // only compiles ok since Go 1.13,
	                 // will cause a panic at run time.
	_ = e << -1      // error: right operand is negative
}

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:
// Results are non-constants.
var a, b uint8 = 255, 1
// Compiles ok, higher overflowed bits are truncated.
var c = a + b  // c == 0
// Compiles ok, higher overflowed bits are truncated.
var d = a << b // d == 254

// Results are untyped constants.
const X = 0x1FFFFFFFF * 0x1FFFFFFFF // overflows int
const R = 'a' + 0x7FFFFFFF          // overflows rune
// The above two lines both compile ok, though the
// two untyped value X and R both overflow their
// respective default types.

// Operation results or conversion results are
// typed values. These lines all 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: 256 overflow uint8

About the results of arithmetic operator operations

Except bitwise shift operations, the result of a binary arithmetic operator operation The rules for the result of a bitwise shift operator operation is a little complicated. Firstly, the result value is always an integer value. Whether it is typed or untyped depends on specific scenarios. Example:
func main() {
	// Three untyped values. Their default
	// types are: int, rune(int32), complex64.
	const X, Y, Z = 2, 'A', 3i

	var a, b int = X, Y // two typed values.

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

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

Another example (bitwise shift operations):
const N = 2
// A is an untyped value (default type as int).
const A = 3.0 << N // A == 12
// B is typed value (type is int8).
const B = int8(3.0) << N // B == 12

var m = uint(32)
// The following three lines are equivalent to
// each other. In the following two lines, the
// types of the two "1" are both deduced as
// int64, instead of int.
var x int64 = 1 << m
var y = int64(1 << m)
var z = int64(1) << m

// The following line fails to compile.
/*
var _ = 1.23 << m // error: shift of type float64
*/

The last rule for bitwise shift operator operation is to avoid the cases that some bitwise shift operations return different results on different architectures but the differences will not be detected in time. For example, if the operand 1 is deduced as int instead of int64, the bitwise operation at line 13 (or line 12) will return different results between 32-bit architectures (0) and 64-bit architectures (0x100000000), which may produce some bugs hard to detect.

One interesting consequence of the last rule for bitwise shift operator operation is shown in the following code snippet:
const n = uint(2)
var m = uint(2)

// The following two lines compile okay.
var _ float64 = 1 << n
var _ = float64(1 << n)

// The following two lines fail to compile.
var _ float64 = 1 << m
var _ = float64(1 << m)
The reason of the last two lines failing to compile is they are both equivalent to the followings two line:
var _ = float64(1) << m
var _ = 1.0 << m // error: shift of type float64

Another example:
package main

const n = uint(8)
var m = uint(8)

func main() {
	println(a, b) // 2 0
}

var a byte = 1 << n / 128
var b byte = 1 << m / 128
The above program prints 2 0, because the last two lines are equivalent to
var a = byte(int(1) << n / 128)
var b = byte(1) << m / 128

About integer division and remainder operations

Assume x and y are two operands of the same integer type, 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 are 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) // compiles okay but panics 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 arithmetic 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 not 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++ // ok. <=> a += 1 <=> a = a + 1
	b-- // ok. <=> b -= 1 <=> b = b - 1
	c++ // ok

	// The following lines fail to compile.
	/*
	_ = a++
	_ = b--
	_ = c++
	++a
	--b
	++c
	*/
}

String Concatenation Operator

As mentioned above, 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 of the two operands are untyped (constant) strings, the result is also an untyped string value.

Boolean (a.k.a. Logical) Operators

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

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

We can use the != operator introduced in the next sub-section 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 of the two operands 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 Generally, the types of its two operands must be the same. For detailed rules, please read comparison rules in Go.

!=

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 untyped boolean value. If both of the two operands of a comparison operation are constant, the result is also a constant (boolean) 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 Precedence

The following is the operator precedence in Go. Top ones have higher precedence. The operators in the same line have the same precedence. Like many other languages, () can be used to promote precedence.

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

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

More About Constant Expressions

The following declared variable will be initialized as 2.2 instead of 2.7. The reason is the precedence of the division operation is higher than the addition operation, and in the division operation, both 3 and 2 are viewed as integers. The evaluation result of 3/2 is 1.
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) // +1.000000e-001
	println(y) // +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 dereference 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.


Index↡

The Go 101 프로젝트는 Github 에서 호스팅됩니다. 오타, 문법 오류, 부정확한 표현, 설명 결함, 코드 버그, 끊어진 링크와 같은 모든 종류의 실수에 대한 수정 사항을 제출하여 Go 101을 개선을 돕는 것은 언제나 환영합니다.

주기적으로 Go에 대한 깊이 있는 정보를 얻고 싶다면 Go 101의 공식 트위터 계정인 @go100and1을 팔로우하거나 Go 101 슬랙 채널에j가입해주세요.

이 책의 디지털 버전은 아래와 같은 곳을 통해서 구매할 수 있습니다.
Go 101의 저자인 Tapir는 2016년 7월부터 Go 101 시리즈 책들을 집필하고 go101.org 웹사이트를 유지 관리하고 있습니다. 새로운 콘텐츠는 책과 웹사이트에 수시로 추가될 예정입니다. Tapir는 인디 게임 개발자이기도 합니다. Tapir의 게임을 플레이하여 Go 101을 지원할 수도 있습니다. (안드로이드와 아이폰/아이패드용):
  • Color Infection (★★★★★), 140개 이상의 단계로 이루어진 물리 기반의 캐주얼 퍼즐 게임
  • Rectangle Pushers (★★★★★), 2가지 모드와 104개 이상의 단계로 이루어진 캐주얼 퍼즐 게임
  • Let's Play With Particles, 세가지 미니 게임이 있는 캐주얼 액션 게임
페이팔을 통한 개인 기부도 환영합니다.

색인: