Go Data Types Explained
Go is a statically typed programming language. In Go, data types are used to declare functions and variables. Data types categorize data based on the memory size required. When programming, you request larger memory only when you need large data, thus making efficient use of memory. During compilation, the compiler needs to know the type of each value so it can determine how much memory to allocate for that value and what the allocated memory represents.
![Go data types overview diagram]
Basic Data Types Overview
| Type | Description |
|---|---|
| uint | 32-bit or 64-bit |
| uint8 | Unsigned 8-bit integer (0 to 255) |
| uint16 | Unsigned 16-bit integer (0 to 65535) |
| uint32 | Unsigned 32-bit integer (0 to 4294967295) |
| uint64 | Unsigned 64-bit integer (0 to 18446744073709551615) |
| int | 32-bit or 64-bit |
| int8 | Signed 8-bit integer (-128 to 127) |
| int16 | Signed 16-bit integer (-32768 to 32767) |
| int32 | Signed 32-bit integer (-2147483648 to 2147483647) |
| int64 | Signed 64-bit integer (-9223372036854775808 to 9223372036854775807) |
| byte | Alias for uint8 (type byte = uint8) |
| rune | Alias for int32 (type rune = int32), represents a Unicode code point |
| uintptr | Unsigned integer type large enough to hold a pointer. Used in low-level programming, especially when interfacing with C libraries or the OS. |
| float32 | IEEE-754 32-bit floating-point number |
| float64 | IEEE-754 64-bit floating-point number |
| complex64 | 32-bit real and imaginary parts |
| complex128 | 64-bit real and imaginary parts |
Integers
Integers are divided into two categories: signed and unsigned.
Signed: int, int8, int16, int32, int64
Unsigned: uint, uint8, uint16, uint32, uint64, byte
The difference between integer types of different bit sizes is the range of numbers they can hold.
Signed types can store any integer, while unsigned types can only store non-negative integers.
The size of int and uint depends on the system: on a 32-bit system they represent int32 and uint32, on a 64-bit system they represent int64 and uint64.
byte is similar to uint8 and is generally used to store a single character.
To ensure program correctness, try to use data types that occupy as little memory as possible.
Use fmt.Printf("%T", varName) to output the type of a variable.
Use unsafe.Sizeof(varName) to check the byte size occupied by a variable.
Floating-Point Numbers
Floating-point numbers are decimal types that can store fractional values like 6.6 or -12.34.
-
Storage of floats: In machine representation, a float consists of a sign bit, exponent bits, and mantissa bits.
-
Precision loss: The mantissa may lose precision. For example:
package main
import "fmt"
func main() {
var num1 float32 = -123.0000901
var num2 float64 = -123.0000901
fmt.Println("num1 = ", num1, "num2 = ", num2)
}
![output showing precision difference]
float64 offers higher precision than float32. For high-precision numbers, choose float64.
-
Float storage consists of three parts: sign bit, exponent bits, and mantissa bits, which may cause precision loss during storage.
-
Go's default floating-point type is
float64. -
Generally, use
float64as it is more precise thanfloat32. -
0.123can be written as.123. Scientific notation is also supported:5.1234e2equals512.34.
Characters
Go does not have a dedicated character type. To store a single character (like a letter), use byte.
A string is a sequence of fixed-length characters. Go strings are composed of individual bytes. Unlike traditional strings composed of characters, Go strings are made of bytes.
- Characters must be enclosed in single quotes (
'), not double quotes. Double quotes are for strings. - When directly outputting a
bytevariable, its ASCII code value is printed. - To output the character itself, use formatted output with
%c. - Go uses UTF-8 encoding. English letters occupy 1 byte, while Chinese characters occupy 3 bytes.
- In Go, a character is essentially an integer. Direct output gives the UTF-8 code point.
- You can assign a numeric value to a variable and output it with
%cto get the corresponding Unicode character. - Character types support arithmetic because they have corresponding Unicode code points.
package main
import "fmt"
func main() {
var c1 byte = 'a'
var c2 byte = '0'
// Direct output prints ASCII value
fmt.Println(c1, "--", c2)
// Formatted output prints the character
fmt.Printf("c1 = %c c2 = %c", c1, c2)
}
![output of character code example]
However, to store characters larger than 255 (e.g., Chinese characters), byte is insuffficient. Use uint or int types instead.
Character Type Essence
-
Storing a character in a computer requires finding its corresponding code point (integer).
- Storage: Character → Code point → Binary → Stored
- Retrieval: Binary → Code point → Character → Displayed
-
The relationship between characters and code points is defined by a character encoding table (standardized).
-
Go uses UTF-8 encoding uniformly, eliminating charset confusion.
Boolean Type
The boolean type (bool) can only take values true or false.
bool occupies 1 byte.
Boolean values are used in logical operations and typically for flow control.
Strings
A string is a sequence of fixed-length characters. Go strings are composed of individual bytes and use UTF-8 encoding to represent Unicode text.
-
Immutability: Once a string is assigned, it cannot be modified. Strings in Go are immutable.
-
String delimiters:
- Double quotes (
"): Escaped characters are interpreted.var str = "abc\nabc" // output contains newline - Backticks (
`): String literal is output as-is, including newlines and special characters. Useful for preventing attacks or outputting source code.var str string = `abc\nabc` // output is literal
- Double quotes (
-
String concatenation uses the
+operator:var str string = "hello " + "world" str += "!" -
Multi-line strings: When a string is too long, break it across lines as follows:
// Correct str := "hello" + " world!" fmt.Println(str) // Incorrect str := "hello " + "world!" fmt.Println(str)
Pointers
- Basic data types store values directly; they are called value types.
- To get the address of a variable, use
&. For example,var num int;&numgives the adress ofnum. - A pointer variable stores an address pointing to the actual value. Example:
var ptr *int = &num. - To access the value a pointer points to, use
*. For example, ifvar ptr *int, then*ptrgives the pointed value.
Pointer details:
- Every value type has a corresponding pointer type of the form
*dataType. For instance,intcorresponds to*int,float64to*float64, etc. - Value types include: basic data types, arrays, and structs.
Value Types vs. Reference Types
Value types: Store values directly; memory is usually allocated on the stack.
Reference types: Store an address that points to the actual data (values); memory is typically allocated on the heap. When no variable references an address, the data becomes garbage and is reclaimed by GC.
Distinction:
- Value types: basic data types (int series, float series, bool, string), arrays, and structs.
- Reference types: pointers, slices, maps, channnels, interfaces.
Default Values of Basic Data Types
In Go, every data type has a default value (zero value) when no value is assigned.
| Data Type | Default Value |
|---|---|
| Integer types | 0 |
| Floating-point types | 0 |
| String | "" (empty string) |
| Boolean | false |
package main
import "fmt"
func main() {
var a int
var b float32
var isTrue bool
var str string
// %v prints the value in its default format
fmt.Printf("a = %v, b = %v, isTrue = %v, str = %v", a, b, isTrue, str)
fmt.Println()
}
Type Conversion Between Basic Types
Unlike Java or C, Go requires explicit conversion when assigning between different types. Data types in Go cannot be automatically converted.
Syntax: type(variable) converts the value variable to type type.
type is the target data type (e.g., int32, int64, float32).
variable is the source variable.
var num int = 42
var float float64 = float64(num)
var ui uint8 = uint8(float)
fmt.Println(num, float, ui)
Important Notes
- Conversion can go from a smaller range to a larger range, or vice versa.
- The conversion is applied to the value stored in the variable; the variable's own type does not change.
- Converting from a larger type to a smaller one (e.g., int64 to int8) compiles without error, but the result may overflow, differing from expectations.
- All conversions must be explicit; automatic conversion is not allowed.
package main
import "fmt"
func main() {
var n1 int32 = 12
var n2 int64
var n3 int8
// n2 = n1 + 20 // Error: int32 to int64 (implicit)
// n3 = n1 + 20 // Error: int32 to int8 (implicit)
n2 = int64(n1) + 20 // Correct
n3 = int8(n1) + 20 // Correct
}
- If an
int8variable starts at 0 and keeps incrementing by 1, its value cycles from 0 to 127, then -128 to -127, and back to 0...127, never exceeding the type's range.
Converting Other Basic Types to String
Often, we need to convert numeric types to strings, or vice versa.
Method 1: fmt.Sprintf
func Sprintf(format string, a ...interface{}) string
Sprintf returns a formatted string based on the format parameter.
package main
import "fmt"
func main() {
var num1 int = 99
var num2 float64 = 23.456
var isTrue bool = true
var char byte = 'A'
var str string
str = fmt.Sprintf("%d", num1)
fmt.Printf("str type: %T, str = %q\n", str, str)
str = fmt.Sprintf("%f", num2)
fmt.Printf("str type: %T, str = %q\n", str, str)
str = fmt.Sprintf("%t", isTrue)
fmt.Printf("str type: %T, str = %q\n", str, str)
str = fmt.Sprintf("%d", char)
fmt.Printf("str type: %T, str = %q\n", str, str)
}
Output:
str type: string, str = "99"
str type: string, str = "23.456000"
str type: string, str = "true"
str type: string, str = "65"
Method 2: Functions from the strconv package
package main
import (
"fmt"
"strconv"
)
func main() {
var num1 int = 99
var num2 float64 = 23.456
var isTrue bool = true
var str string
str = strconv.FormatInt(int64(num1), 10)
str = strconv.Itoa(num1)
fmt.Printf("str type: %T, str = %q\n", str, str)
str = strconv.FormatFloat(num2, 'f', 10, 64)
fmt.Printf("str type: %T, str = %q\n", str, str)
str = strconv.FormatBool(isTrue)
fmt.Printf("str type: %T, str = %q\n", str, str)
}
Output:
str type: string, str = "99"
str type: string, str = "23.4560000000"
str type: string, str = "true"
Converting String to Other Basic Types
Method: Use functions from the strconv package.
package main
import (
"fmt"
"strconv"
)
func main() {
var str string = "true"
var str1 string = "123456"
var str2 string = "123.456"
var isTrue bool
var num int64
var num2 float64
isTrue, _ = strconv.ParseBool(str)
fmt.Printf("type: %T, value: %v\n", isTrue, isTrue)
num, _ = strconv.ParseInt(str1, 10, 64)
fmt.Printf("type: %T, value: %v\n", num, num)
num2, _ = strconv.ParseFloat(str2, 64)
fmt.Printf("type: %T, value: %v\n", num2, num2)
}
Output:
type: bool, value: true
type: int64, value: 123456
type: float64, value: 123.456
Caution: When converting a string to another basic type, ensure the string represents valid data. For example, converting "123" to an integer works, but converting "hello" to an integer yields 0. Similarly for other types: invalid float strings produce 0.0, invalid bool strings produce false.