Go006 Golang Builtin Types and Value Literals

Types can be viewed as value template, and values can be viewd as type’s instances.

Basic builtin types in go

There are 17 basic builtin types in golang, we can use these types in code
without import any package, though there name is non-exported identifier,
they are:
Boolen type(1): bool
Numeric types:

  • integer(11) : int8, uint8; int16, uint16; int32, uint32; int64, uint64; int, uint, uintptr
  • float(2): float32, float64

Complex types(2): complex64, complex128
String type(1): string

There are two type aliases in golang:
byte is the alias of uint8, we can view both of them as the same type
rune is ther alias of int32, we can view both of them as the same type

Numeric interger type whose name start with u indicate it’s a unsigned interger,
means it never negtive. And the number in it’s name indicate the binary bites
occupied in memory at runtime. eg:

 1var u64 uint64
 2//      |   |
 3//      |  '64' means it will cooupy 64bit space in memory when code run
 4//     'u' means a unsigned interger
 5
 6// i8 and ui8 will occupy 8 bit(1 byte) space in memory at runtime
 7var i8 int8 // (-2^8 ~ -2^8-1)
 8var ui8 uint8 // (0 ~ 2^7-1)
 9
10// i64 will occupy 64 bit(8 bytes) space in memory at runtime
11var i64 int64
12
13// int,uint and uintptr are implementation-specific
14// in/uin will cooupy 32 bit(4 bytes) space when code run at a 32bit arch system
15// in/uin will cooupy 64 bit(8 bytes) space when code run at a 64bit arch system
16var in int
17var uin uint
18
19// uinptr whil big enough to store any memory address
20var uinptr uintptr

In complex type, the real and imaginary part of complex64 is a type of float32;
the real and imaginary part of complex128 is a type of float64, eg:

1// here 1 and 2i is float32
2var cpx64 complex64 = 1 + 2i
3
4// here 1 and 2i are float64
5var cpx128 complex64 = 1 + 2i

Zero values

Every value of a type got it’s zero value when declared but not initizlized,
zero value can be viewed as the default value of the type:

 1// standard define
 2var name string
 3var isBoy bool
 4
 5var age uint8 //(byte)
 6// byte is a built-in alias of uint8
 7var ageAlias byte
 8var temp int8
 9
10var ageb uint16
11var tempb int16
12
13var beard uint32
14var beardb int32 // (rune)
15// rune is a built-in alias of int32
16var beardbAlias rune
17
18var hair uint64
19var hairb int64
20
21var intv int
22var uintv uint
23var uintpv uintptr
24
25var salarys float32
26var salaryb float64
27
28var imgvs complex64
29var imgvb complex128
30
31var bo bool
32var str string
33/* default value
34fmt.Printf("%#v\n%#v\n%#v\n%#v\n%#v\n%#v\n%#v\n%#v\n%#v\n%#v\n%#v\n%#v\n%#v\n%#v\n%#v\n%#v\n%#v\n%#v\n%#v\n%#v\n%#v\n",
35name, isBoy, age, ageAlias, temp, ageb, tempb, beard, beardb, beardbAlias, hair, hairb, intv, uintv, uintpv, salarys, salaryb, imgvs, imgvb, bo, string)
36
37""
38false
390x0
400x0
410
420x0
430
440x0
450
460
470x0
480
490
500x0
510x0
520
530
54(0+0i)
55(0+0i)
56*/

As we can see above:

  • default value of boolean is false
  • default value of numeric is 0 of different memory size
  • default value of string is a empty string ""

Basic value literals

A literal of a value is the text representation of the value in code, a single
value may have multi representations(multi literals).

Boolean value literals: go specifications does not metioned boolean literals,
but we can view the predifined identifiers of false and true, as boolean
literals. But we should know that the two are not literals in the strict sense.

Integer value literals: we can represent a integer value literal in 4 forms,
the binary(base 2) form, the Octal(base 8) form, the Hex(base 16) form and the
Decimal(base 10) form. The following literals all represent 27 in decimal:

 1// the Binary form, starts with a '0b' or '0B'
 20b11011 // or 0b_11011 for readability
 30B11011
 4
 5// the Octal form, starts with a '0' or '0o' or '0O'
 6033
 70o33 // or 0o_33
 80O33
 9
10// the Hexadecimal form, start with a '0x' or '0X'
110x1b
120X1B
13
14// the Decimal form, starts without a '0'
1527
16
17// all true
18fmt.Println(0b11011 == 033)
19fmt.Println(0x1b == 033)
20fmt.Println(27 == 033)

(Note: the binary form and the octal from starting with 0o or 0O are supported since Go 1.13.)

Float-point value literals:

Float-point value literals may contain four part: a decimail integer part, a decimal
point, a decimal fractional part, and an interger exponent part(10-based, starts
with a letter e or E and suffixes with a decimal integer, eg: 2E3 = 2 * 10^3,
2E-3 = 2 / 10^3). Below are all valid float-point value literals:

 11.23e45 // 1.23 * 10^45
 21e-4
 32.E3 // 2 * 10^3 = 2000
 42.E+3
 52.E-3
 61.32
 701.32
 8001.32
 9.23
101.
11.3E0 // 0.3 * 10^0 = 0.3
120010e-2 // 10 / 10^2 = 0.1
130e5 // 
14
15//----------------------- eg:
16a := 000000000000000000000000000101   // Octal int
17b := 000000000000000000000000000101e0 // Decimal float-point
18fmt.Printf("%#T,%#v\n%#T,%#v\n", a, a, b, b)
19/* output:
20int,65
21float64,101
22*/
23
24// Note: this is an expression of (hexdecimal - int)
25e := 0x12e-3  // 1*16^2 + 2*16 + 14 - 3 = 299
26fmt.Printf("%#T: %#v\n", e, e)
27/* output
28int: 299
29*/
30
31//----------------------- eg:
32package main
33
34import (
35	"fmt"
36)
37
38func main() {
39	fmt.Printf("%#T, %#v\n", 01.23, 01.23)
40	fmt.Printf("%#T, %#v\n", 001.23, 001.23)
41	fmt.Printf("%#T, %#v\n", 1.23e12, 001.23e12)
42	fmt.Printf("%#T, %#v\n", .23e2, .23e3)
43	fmt.Printf("%#T, %#v\n", 1., 1.)
44	fmt.Printf("%#T, %#v\n", 2.e3, 2.e3)
45	fmt.Printf("%#T, %#v\n", 2.e-3, 2.e-3)
46	fmt.Printf("%#T, %#v\n", 00101e-2, 00101e-2)
47	fmt.Printf("%#T, %#v\n", .3e0, .3e0)
48	fmt.Printf("%#T, %#v\n", 0e5, 0e5)
49}
50
51/* output: 
52float64, 1.23
53float64, 1.23
54float64, 1.23e+12
55float64, 230
56float64, 1
57float64, 2000
58float64, 0.002
59float64, 1.01
60float64, 0.3
61float64, 0
62*/

From Go 1.13, Go also supports hexadecimal floating literal form:

  • Hexadecimal floating point literal must end with 2-based exponent part, which starts
    whit a letter p or P and suffixes with a decimal integer literal(mPn = m * 2^n,
    mP-n = m / 2^n)
  • It’s hexadecimal, so it must start wit a 0x or 0X

There are some legal hexadecimal float point literals:

 1a := 0x3.2p2   // (3 + 2/16) * 2^2 = 12.5
 2aa := 0x3.2p-2 // (3 + 2/16) / 2^2 = 0.78125
 3b := 0x.3p2    // (3/16) * 2^2 = 0.75
 4c := 0x2.p3    // 2 * 2^3 = 16
 5d := 0x.4p-2   // (4/16) / 2^2 = 1/16
 6fmt.Printf("%#T: %#v\n", a, a)
 7fmt.Printf("%#T: %#v\n", aa, aa)
 8fmt.Printf("%#T: %#v\n", b, b)
 9fmt.Printf("%#T: %#v\n", c, c)
10fmt.Printf("%#T: %#v\n", d, d)
11/*  output:
12float64: 12.5
13float64: 0.78125
14float64: 0.75
15float64: 16
16float64: 0.0625
17*/
18
19e := 0x12e-3  // 1*16^2 + 2*16 + 14 - 3 = 299
20fmt.Printf("%#T: %#v\n", e, e)
21/* output
22int: 299
23*/

Imaginary value literals:

Imaginary literal of golang is a float-point literal or integer literal followed by
a lower case i. Examples:

 1fmt.Println(1i)
 2fmt.Println(1.i)
 3fmt.Println(.1i)
 4fmt.Println(1.23i)
 5fmt.Println(0b11i)
 6fmt.Println(0o11i)
 7fmt.Println(0x12i)
 8fmt.Println(2e-2i)
 9fmt.Println(1.2e+2i)
10fmt.Println(0123i) // (for backward-compatibility. octal literal suffix with i)
11
12// only go1.13 and later can pass compile
13fmt.Println(0x1.8p-0i) // (16/16+8/16) * 10^0 = 1.5(base10)
14
15/* output:
16   Note: these outputs are all complex literals
17(0+1i)
18(0+1i)
19(0+0.1i)
20(0+1.23i)
21(0+3i)
22(0+9i)
23(0+18i)
24(0+0.02i)
25(0+120i)
26(0+123i)
27(0+1.5i)
28*/

Imaginary literals are used to represent the imaginary part of complex value literal
here are some complex value literals:

11 + 2i //  (1+2i)
2.1 + 2.i // (0.1+2i)
31. + .2i // (1+0.2i)
41.1 - .1i // (1.1-0.1i)
50x.5p2i-1.5e+2 // -150 + 1.25i
6
7// standard complex zero value
80. + 0.0i

Use _ in code for better readability:

Since Go1.13, underscore can appear in integer, floating-point and imaginary
literals as a digit separator for better readability, but there are rules:

  1. _ not allowed to be the first and last character
  2. both sides on _ must be either a literal prefix(0X,0b, 0o) or a legal
    digit character

There are some legal examples:

1fmt.Println(1_2)
2fmt.Println(1.2_3)
3fmt.Println(0x_abc_dea_bcd == 0xabc_dea_bcd)
4fmt.Println(0x_a_bp-2 == 0x_a_bp-2)
5fmt.Println(001_2_3 == 01_2_3)
6fmt.Println(0b_11_00_11 == 0b11_00_11)
7fmt.Println((0x1.8p-1 - 0x1_4) == (-0x01_4 + 0x_1.8p-1))

There are some illegal examples:

1_12 // can't be first char
234_ // can't be last char
30_x12 // 'x' is not a legal digit
41_.2 // '.' is not a legal digit
52._1 // '.' is not a legal digit
62__2 // two continuous underscores

Rune value literals:
A rune value is intended to store a Unicode code point. Generally, we can view
a code point as a Unicode character, but we should know that some Unicode characters
are composed of more than one code points each.

A rune literal is expressed as one or more characters enclosed in a pair of quotes.
The enclosed characters denote one Unicode code point value. There are some minor
variants of the rune literal form. The most popular form of rune literals is just to
enclose the characters denoted by rune values between two single quotes. For example:

1'b' // an English character
2'π'
3'众' // a Chinese character

the following literals are equivalent to character b (Unicode value of b is 98):

 1// hex representation of decimal 98
 2fmt.Printf("%U, %c\n", '\x62', '\x62')
 3// octal representation of decimal 98
 4fmt.Printf("%U, %c\n", '\142', '\142')
 5// unicode format(4 digit)
 6fmt.Printf("%U, %c\n", '\u0062', '\u0062')
 7// unicode format (8 digit)
 8fmt.Printf("%U, %c\n", '\U00000062', '\U00000062')
 9
10/*
11U+0062, b
12U+0062, b
13U+0062, b
14U+0062, b
15*/

Note:

  1. \ must follow exact 3 octal digits to represent a byte value
  2. \x must follow exact 2 hex digits to represent a byte value
  3. \u must follow exact 4 hex digits to represent a rune value
  4. \U must follow exact 8 hex digits to represent a rune value

Each such octal or hex digit sequence must represent a legal Unicode code point,
otherwise, it fails to compile.

The following program will print 7 true texts:

 1package main
 2
 3func main() { 
 4	println('b' == 98)
 5	println('b' == '\142') // '\' must follow 3 octal digits
 6	println('b' == '\x62') // '\x' must follow 2 hex digits
 7	println('b' == '\u0062') // '\u' must follow 4 hex digits
 8	println('b' == '\U00000062') // '\U' must follow 8 hex digits
 9	println(0x62 == '\x62')
10	println('\x9053' == '道')
11}

If a rune literal is composed by two characters (not including the two quotes):

  • the first one is the character \
  • the second one is not a digital character, x, u and U
    then the the two characters will be escaped as one special character. The possible
    character pairs to be escaped are:
1\a   (Unicode value 0x07) alert or bell
2\b   (Unicode value 0x08) backspace
3\f   (Unicode value 0x0C) form feed
4\n   (Unicode value 0x0A) line feed or newline
5\r   (Unicode value 0x0D) carriage return
6\t   (Unicode value 0x09) horizontal tab
7\v   (Unicode value 0x0b) vertical tab
8\\   (Unicode value 0x5c) backslash
9\'   (Unicode value 0x27) single quote

\n is the most used escape character pair.
Examples:

 1fmt.Printf("%T, %#v\n", '\a', '\a')
 2fmt.Printf("%T, %#v\n", '\b', '\b')
 3fmt.Printf("%T, %#v\n", '\f', '\f')
 4fmt.Printf("%T, %#v\n", '\n', '\n')
 5fmt.Printf("%T, %#v\n", '\r', '\r')
 6fmt.Printf("%T, %#v\n", '\t', '\t')
 7fmt.Printf("%T, %#v\n", '\v', '\v')
 8fmt.Printf("%T, %#v\n", '\\', '\\')
 9fmt.Printf("%T, %#v\n", '\'', '\'')
10fmt.Println('\n' == 10)     // true
11fmt.Println('\n' == '\x0A') // true

The zero value of a rune also can be represented by other numeric type literals:

 1fmt.Println('\x00' == 0e0)
 2fmt.Println('\000' == 0.0)
 3fmt.Println('\u0000' == 0i)
 4fmt.Println('\U00000000' == 0x0p0)
 5
 6/* output:
 7true
 8true
 9true
10true
11*/

String value literals: Golang’s string values are UTF-8 encoded. In fact, all golang source files must be UTF-8 encoding
compatible. There two forms of string literals:

  • Interpreted string literal(double quotes form)
  • Raw string literal(back quotes form) For example, two strings follow are equivalent:
 1// Interpreted string literal
 2"Hello\n你好!\nHola"
 3
 4// Raw string literal
 5`Hello
 6你好!
 7Hola`
 8
 9// example
10var s1 string = "Hello\n你好!\nHola \` \"jack\""  // \n excaped to a newline character
11var s2 string = `Hello
12你好!
13Hola "jack"`
14fmt.Println(s1)
15fmt.Println(s2)
16fmt.Println(s1 == s2)
17/* output
18Hello
19你好!
20Hola "jack"
21===========
22Hello
23你好!
24Hola "jack"
25===========
26true
27*/
28
29// Note: \` is only valid in rune context
30// and \" is only valid in Interpreted string literal
31"hello \` ni hao"  // compiler error: unknown escape
32`hello \" ni hao`  // no error, but \" won't be interpreted

The character sequence start with \, \x, \u and \U and followed by some octal or
hex digits can also be used in string literals:

 1var a rune = '你'
 2var b rune = '好'
 3
 4var z string = "\141\142\143"
 5var y string = "\u4f60\u597d"
 6var yy string = "\U00004f60\U0000597d"
 7var x string = "\x39\x63"
 8
 9fmt.Printf("%x\n", a)
10fmt.Printf("%x\n", b)
11fmt.Printf("%T, %#v\n", z, z)
12fmt.Printf("%T, %#v\n", y, y)
13fmt.Println(y == yy)
14fmt.Printf("%T, %#v\n", x, x)
15
16/* output:
174f60
18597d
19string, "abc"
20string, "你好"
21true
22string, "9c"
23*/

Note:

  • Each English character (code point) is represented with one byte, but each Chinese character
    (code point) is represented with three bytes.
  • In a raw string literal, no character sequences will be escaped.
  • The back quote character ‘\`’ is not allowed to appear in a raw string literal.
  • To get better cross-platform compatibility, carriage return characters ‘\n’ (Unicode code point 0x0D) inside raw string literals will be discarded.

Zero values of string types can be denoted as "" or `` in literal.

1fmt.Println("" == ``)
2/* output:
3true
4*/

Notes:

  • Because no values of the basic integer types provided in Go can hold 0x10000000000000000(16 zeros), so the literal is not representable as values of any basic integer types.
  • The maximum IEEE-754 float32 value which can be represented accurately is
    3.40282346638528859811704183484516925440e+38, so 3.5e38 is not representable as values of any
    float32 and complex64 types.
  • The max IEEE-754 float64 value which can be represented accurately is
    1.797693134862315708145274237317043567981e+308, so 2e+308 is not representable as values of
    any float64 and complex128 types.

In the end, please note, although 0x10000000000000000(16 zeros) can represent values of float32 types,
however it can’t represent any float32 values accurately in memory. In other words, it will be
rounded to the closest float32 value which can be represented accurately in memory when it is
used as values of float32 types.

1fmt.Println(len("0x10000000000000000"))
2fmt.Println(float32(0x10000000000000000))
3/* output:
419
51.8446744e+19
6*/

Go005 Golang Keywords and Identifiers
Go007 Golang Constants and Variables