Introduction
As a strong type static language, Go has many primitive types we will care about. In first level, they can be divided into two groups: non-collection and collections(e.g. array, slice, map). In this article we will talk three different non-collection primitive types in Go.
1. Boolean
2. Numerical (integer, float and complex number)
3. Text (string and rune)
Boolean
1. Use true and false for boolean
This may sounds crystal forward, but in Go we can only use keywords true & false for boolean type. Not like in some other language we can use 3 or 11 or 19 for true, 0 for false.
For example:
func main() { r := 36 for 3 { fmt.Printf("%v, %T\n", r, r) } } // output // prog.go:9:2: non-bool 3 (type untyped number) used as for condition
Numerical
1. Integer
a) Interger can be sign interger or unsign interger
sign integers, there are 5 versions:
int: has varying size, but mininal int32.
int8: -128 ~ 127
int16: -32,768 ~ 32,767
int32: around 2 billion
int64: vary large
use big package from math library can set more larger number
unsign integer, there are four versions:
uint, uint8, uint16, uint32, we don't have int64.
b) calculations
There are five kinds of calculation we can do in integer: add, substract, multiple, divise, remainder(remainder only exist in integer)
Integer calculation will result integer, the remainder will miss. We can use % operator to get remainder between integers.
func main() { a := 3 b := 10 fmt.Println(b / a) fmt.Println(b % a) } // output // 3, remainder is missing // 1, get remainder using % operator
Notice integers in same family but with different type can not do caculateion. e.g. int8 + int16 = error
c) bitwise calculations
To keep thing simple, we don't cover bitwise calculations for now.
2.Floating points
a) There are two version of floating points, float32 and float64.
We can use decimal(e.g. 3.14) or exponential(3.14e12, or 3.14E12).
func main() { a := 10.1 b := 10.1E12 fmt.Printf("%v, %T\n", a, a) fmt.Printf("%v, %T", b, b) } // output // 10.1, float64 // 1.01e+13, float64
b) calculations
Except %(remainder operator), they are: add, substract, multiple, divise. And floating points don't have bitwise calculation.
3. complex number
There are not so much language design complex number as a build-in type. From this we can get a glimps of how powerful Go is in science.
a) two versions, complex64 and complex128.
In math, we use float + float i to represent a complex number. So complex number is float32 + float32 = complex64 or float64 + float64 = complex128
func main() { a := 1 + 2i b := complex(1, 1.2) fmt.Printf("%v, %T\n", a, a) fmt.Printf("%v, %T", b, b) } // output // (1+2i), complex128 // (1+1.2i), complex128
b) build-in functions
complex() to build a complex number.
real() to get the real part of a complex number.
imag() to get the imaginary part of a complex number.
func main() { a := 1 + 2i b := complex(1, 1.2) fmt.Printf("%v, %T\n", a, a) fmt.Printf("%v, %T\n", b, b) fmt.Printf("%v, %T\n", real(b), real(b)) fmt.Printf("%v, %T\n", imag(b), imag(b)) } // output // (1+2i), complex128 // (1+1.2i), complex128 // 1, float64 // 1.2, float64
c) calculations
They are: add, substract, multiple, divise. Notice, in math, multiplication and division of two complex number is defined as polynomial style.
Text
1. string
String in Go stands for any utf8 character. It's powerful but also makes string can not encode all character available(for that, we have rune).
a) string, collection of letters
String behaves sort of like an array. That is, a collection of letters. We can use [] operator to slice them.
func main() { s := "This is a string" fmt.Printf("%v, %T", s[2], s[2]) } // output // 105, uint8
This result is out of our expect. We are expecting a letter "i". Actually, when we slice a string, in other language we may can get letters, in Go we will get the number of utf8/ ascii code. 105 stands for "i" in uft8/ ascii. And uint8 is alias for byte in Go.
We can use string() to change it back to a string.
func main() { s := "This is a string" fmt.Printf("%v, %T", string(s[2]), string(s[2])) } // output // i, string
b) string is immutable
Although a string can be slice like an array, but we can't change the value inside.
func main() { s := "This is a string" s[2] = "u" fmt.Printf("%v, %T", string(s[2]), s[2]) } // two errors: // first, can not assign a string "u" to byte s[2] // second, can not manipulate the value inside a string
c) concat two string with +
A most common task is to concat two string together, in Go we can use operator +.
func main() { s := "This is a string" s2 := "This is also a string" fmt.Println(s + s2) } // output // This is a stringThis is also a string
d) convert string to collection of byte
We can use []byte() to conver a string to collection of bytes. This can make our application work more easily with other applications than we only use string.
func main() { s := "This is a string" b := []byte(s) fmt.Printf("%v, %T", b, b) } // output // [84 104 105 115 32 105 115 32 97 32 115 116 114 105 110 103], []uint8
The result shows a collection of integer numbers, that is ascii/utf8 value for each letter and space in our string s. As we said before, uint8 is alias for byte, so this []uint8 means []byte.
We can exchange []byte and string back and forward as we need without any effort. In some situation, we may say string is collection of byte([]byte), and vice versa.
func main() { s := "This is a string" b := []byte(s) fmt.Printf("%v, %T\n", b, b) fmt.Printf("%v, %T", string(b), string(b)) } // output // [84 104 105 115 32 105 115 32 97 32 115 116 114 105 110 103], []uint8 // This is a string, string
2.rune
a) use single quote to declare a rune
In Go, we use double quote " to declare a string and use single quote ' to decalre a rune.
A rune can only be one character. var r rune = 'aa' will return error.
func main() { var r rune = 'a' r2 := 'a' fmt.Printf("%v, %T\n", r, r) fmt.Printf("%v, %T", r2, r2) } // output // 97, int32 // 97, int32
In the result we can see the type is int32, that means rune is alias for int32.
b) why we need rune
Slicing a string may result wrong when it is not an english string.
As we can see in the first for-loop, each s[i] is a uint8(byte). That is, out application recognize Chinese character(hanzi) as english letter. But each Chinese character is 3 bytes not 1 byte(english letter). So the second for-loop output completely wrong.
func main() { var s string = "劳动光荣" b := []byte(s) fmt.Printf("%v, %T\n", b, b) fmt.Printf("%v, %T\n", string(b), string(b)) for i := 0; i < len(s); i++ { fmt.Printf("%v, %T\n", s[i], s[i]) } for i := 0; i < len(s); i++ { fmt.Printf("%v, %T\n", string(s[i]), string(s[i])) } } // output [229 138 179 229 138 168 229 133 137 232 141 163], []uint8 劳动光荣, string 229, uint8 138, uint8 179, uint8 229, uint8 138, uint8 168, uint8 229, uint8 133, uint8 137, uint8 232, uint8 141, uint8 163, uint8 å, string , string ³, string å, string , string ¨, string å, string , string , string è, string , string £, string
If we use rune, our application can recognize non-english character well. In for-loop, we have range function to get each rune of non-english character.
Each output of first for-loop is int32, which means rune.
func main() { var s string = "劳动光荣" for i, v := range s { fmt.Printf("%v, %v, %T\n", i, v, v) } for i, v := range s { fmt.Printf("%v, %v, %T\n", i, string(v), string(v)) } } // output 0, 21171, int32 3, 21160, int32 6, 20809, int32 9, 33635, int32 0, 劳, string 3, 动, string 6, 光, string 9, 荣, string
Summary
To keep things simple, there are three notice from this article:
1. Can only use keyword true and false for boolen type.
2. Can not calculate different type number even they are in same family. That is int8 + int32 = error.
3. Care about non-english string, we might need help of rune.