Go Data Types
Go is a statically typed language, all variables have definite types. This chapter will detail Go's type system, including basic types, composite types, and custom types.
🎯 Type System Overview
Go Language Type Classification
Go Data Types
├── Basic Types (Basic Types)
│ ├── Numeric Types
│ │ ├── Integer Types (int, int8, int16, int32, int64)
│ │ ├── Unsigned Integers (uint, uint8, uint16, uint32, uint64)
│ │ ├── Floating-Point (float32, float64)
│ │ └── Complex (complex64, complex128)
│ ├── String Type (string)
│ └── Boolean Type (bool)
├── Composite Types (Composite Types)
│ ├── Array (Array)
│ ├── Slice (Slice)
│ ├── Map (Map)
│ ├── Struct (Struct)
│ ├── Pointer (Pointer)
│ ├── Function (Function)
│ ├── Interface (Interface)
│ └── Channel (Channel)
└── Custom Types (Custom Types)
└── Type Aliases and Type Definitions🔢 Numeric Types
Integer Types
Signed Integers
go
package main
import (
"fmt"
"unsafe"
)
func main() {
// Signed integer types
var i8 int8 = 127 // 8-bit: -128 to 127
var i16 int16 = 32767 // 16-bit: -32768 to 32767
var i32 int32 = 2147483647 // 32-bit: -2147483648 to 2147483647
var i64 int64 = 9223372036854775807 // 64-bit: larger range
// int type size depends on platform (32-bit or 64-bit)
var i int = 42
fmt.Printf("int8: %d, size: %d bytes\n", i8, unsafe.Sizeof(i8))
fmt.Printf("int16: %d, size: %d bytes\n", i16, unsafe.Sizeof(i16))
fmt.Printf("int32: %d, size: %d bytes\n", i32, unsafe.Sizeof(i32))
fmt.Printf("int64: %d, size: %d bytes\n", i64, unsafe.Sizeof(i64))
fmt.Printf("int: %d, size: %d bytes\n", i, unsafe.Sizeof(i))
}Unsigned Integers
go
func main() {
// Unsigned integer types (can only represent non-negative numbers)
var ui8 uint8 = 255 // 8-bit: 0 to 255
var ui16 uint16 = 65535 // 16-bit: 0 to 65535
var ui32 uint32 = 4294967295 // 32-bit: 0 to 4294967295
var ui64 uint64 = 18446744073709551615 // 64-bit: larger range
// uint type size depends on platform
var ui uint = 42
// Special types
var b byte = 255 // byte is equivalent to uint8
var r rune = '中' // rune is equivalent to int32, used to represent Unicode characters
var ptr uintptr = 0x1000 // uintptr is used to store pointer addresses
fmt.Printf("uint8: %d\n", ui8)
fmt.Printf("byte: %d\n", b)
fmt.Printf("rune: %d (%c)\n", r, r)
fmt.Printf("uintptr: 0x%x\n", ptr)
}Integer Literals
go
func main() {
// Integer literals in different bases
var dec = 42 // Decimal
var bin = 0b101010 // Binary (Go 1.13+)
var oct = 0o52 // Octal
var hex = 0x2A // Hexadecimal
// Use underscores for readability
var million = 1_000_000
var binary = 0b1010_1010_1010_1010
fmt.Printf("Decimal: %d\n", dec)
fmt.Printf("Binary: %d\n", bin)
fmt.Printf("Octal: %d\n", oct)
fmt.Printf("Hexadecimal: %d\n", hex)
fmt.Printf("Million: %d\n", million)
}Floating-Point Types
Basic Floating-Point
go
import "math"
func main() {
// Floating-point types
var f32 float32 = 3.14159
var f64 float64 = 3.141592653589793
// Default floating-point type is float64
var pi = 3.14159 // Type is float64
fmt.Printf("float32: %.5f, size: %d bytes\n", f32, unsafe.Sizeof(f32))
fmt.Printf("float64: %.15f, size: %d bytes\n", f64, unsafe.Sizeof(f64))
// Special floating-point values
fmt.Printf("Positive infinity: %f\n", math.Inf(1))
fmt.Printf("Negative infinity: %f\n", math.Inf(-1))
fmt.Printf("NaN: %f\n", math.NaN())
// Floating-point precision issues
fmt.Printf("0.1 + 0.2 = %.17f\n", 0.1+0.2)
}Floating-Point Literals
go
func main() {
// Floating-point literals in different forms
var f1 = 3.14 // Decimal form
var f2 = .25 // Omit integer part
var f3 = 1. // Omit decimal part
var f4 = 1e6 // Scientific notation: 1 * 10^6
var f5 = 2.5e-3 // Scientific notation: 2.5 * 10^-3
var f6 = 0x1.Fp+0 // Hexadecimal floating-point
fmt.Printf("f1: %g\n", f1)
fmt.Printf("f2: %g\n", f2)
fmt.Printf("f3: %g\n", f3)
fmt.Printf("f4: %g\n", f4)
fmt.Printf("f5: %g\n", f5)
fmt.Printf("f6: %g\n", f6)
}Complex Types
go
func main() {
// Complex types
var c64 complex64 = 1 + 2i // Both real and imaginary parts are float32
var c128 complex128 = 3 + 4i // Both real and imaginary parts are float64
// Use complex function to create complex numbers
var c1 = complex(1.5, 2.5) // complex128
var c2 = complex(float32(1), float32(2)) // complex64
// Get real and imaginary parts
fmt.Printf("c128: %v\n", c128)
fmt.Printf("Real: %g, Imaginary: %g\n", real(c128), imag(c128))
// Complex operations
fmt.Printf("c1 + c2: %v\n", c1+complex128(c2))
fmt.Printf("c1 * c2: %v\n", c1*complex128(c2))
}📝 String Type
String Basics
go
func main() {
// String declaration
var str1 string = "Hello, 世界!"
var str2 = "Go Language"
str3 := "Simple and Efficient"
// Strings are immutable
fmt.Printf("String: %s\n", str1)
fmt.Printf("Length: %d bytes\n", len(str1)) // Note: len returns bytes, not characters
// String concatenation
result := str2 + " " + str3
fmt.Printf("Concatenation result: %s\n", result)
// Raw string (backticks)
rawStr := `This is a raw string
Can contain newlines
And special characters \n \t`
fmt.Printf("Raw string:\n%s\n", rawStr)
}String Operations
go
import (
"fmt"
"strings"
"unicode/utf8"
)
func main() {
str := "Hello, 世界!"
// Byte length vs character length
fmt.Printf("Byte length: %d\n", len(str))
fmt.Printf("Character length: %d\n", utf8.RuneCountInString(str))
// String iteration
fmt.Println("Iterate by bytes:")
for i := 0; i < len(str); i++ {
fmt.Printf("%d: %c\n", i, str[i])
}
fmt.Println("Iterate by characters:")
for i, r := range str {
fmt.Printf("%d: %c\n", i, r)
}
// String slicing
fmt.Printf("First 5 bytes: %s\n", str[:5])
fmt.Printf("From byte 7: %s\n", str[7:])
// String contains
fmt.Printf("Contains '世界': %t\n", strings.Contains(str, "世界"))
fmt.Printf("Prefix 'Hello': %t\n", strings.HasPrefix(str, "Hello"))
}Character Type (rune)
go
func main() {
// rune type represents a Unicode character
var r1 rune = 'A' // ASCII character
var r2 rune = '中' // Chinese character
var r3 rune = '🚀' // Emoji character
// Unicode escapes
var r4 rune = '\u4e2d' // Unicode: 中
var r5 rune = '\U0001F680' // Unicode: 🚀
fmt.Printf("r1: %c (value: %d)\n", r1, r1)
fmt.Printf("r2: %c (value: %d)\n", r2, r2)
fmt.Printf("r3: %c (value: %d)\n", r3, r3)
fmt.Printf("r4: %c (value: %d)\n", r4, r4)
fmt.Printf("r5: %c (value: %d)\n", r5, r5)
// String to rune slice
str := "Hello, 世界!"
runes := []rune(str)
fmt.Printf("Character array: %v\n", runes)
fmt.Printf("8th character: %c\n", runes[7])
}✅ Boolean Type
go
func main() {
// Boolean type has only two values: true and false
var b1 bool = true
var b2 bool = false
var b3 = true // Type inference
b4 := false // Short variable declaration
// Zero value of boolean type is false
var b5 bool
fmt.Printf("Boolean zero value: %t\n", b5)
// Boolean operations
fmt.Printf("true && false: %t\n", true && false)
fmt.Printf("true || false: %t\n", true || false)
fmt.Printf("!true: %t\n", !true)
// Comparison operations return boolean values
fmt.Printf("5 > 3: %t\n", 5 > 3)
fmt.Printf("\"abc\" == \"def\": %t\n", "abc" == "def")
// Boolean values cannot be directly converted to numbers
// var i int = b1 // Error!
// Need explicit conversion
var i int
if b1 {
i = 1
} else {
i = 0
}
fmt.Printf("Boolean to integer: %d\n", i)
}🎨 Type Zero Values
Zero Value Concept
go
func main() {
// All types in Go have zero values (default values)
var i int // 0
var f float64 // 0.0
var b bool // false
var s string // "" (empty string)
var p *int // nil
var slice []int // nil
var m map[string]int // nil
var ch chan int // nil
var fn func() // nil
fmt.Printf("int zero value: %d\n", i)
fmt.Printf("float64 zero value: %g\n", f)
fmt.Printf("bool zero value: %t\n", b)
fmt.Printf("string zero value: %q\n", s)
fmt.Printf("pointer zero value: %v\n", p)
fmt.Printf("slice zero value: %v\n", slice)
fmt.Printf("map zero value: %v\n", m)
fmt.Printf("channel zero value: %v\n", ch)
fmt.Printf("function zero value: %v\n", fn)
}🔄 Type Conversion
Basic Type Conversion
go
import "strconv"
func main() {
// Conversion between numeric types (must be explicit)
var i int = 42
var f float64 = float64(i) // int to float64
var u uint = uint(f) // float64 to uint
fmt.Printf("int: %d, float64: %g, uint: %d\n", i, f, u)
// String and number conversion
str := "123"
num, err := strconv.Atoi(str) // String to integer
if err != nil {
fmt.Printf("Conversion error: %v\n", err)
} else {
fmt.Printf("String %s to integer: %d\n", str, num)
}
// Number to string
numStr := strconv.Itoa(456)
fmt.Printf("Integer 456 to string: %s\n", numStr)
// Float conversion
floatStr := strconv.FormatFloat(3.14159, 'f', 2, 64)
fmt.Printf("Float to string: %s\n", floatStr)
pi, err := strconv.ParseFloat("3.14159", 64)
if err == nil {
fmt.Printf("String to float: %g\n", pi)
}
}Type Assertion
go
func main() {
// Type assertion for interface types
var i interface{} = 42
// Safe type assertion
if v, ok := i.(int); ok {
fmt.Printf("i is int type, value: %d\n", v)
}
// Type assertion failure case
if v, ok := i.(string); ok {
fmt.Printf("i is string type, value: %s\n", v)
} else {
fmt.Println("i is not string type")
}
// Dangerous type assertion (may panic)
// v := i.(string) // Will panic if i is not string type
}📊 Type Information
Get Type Information
go
import (
"fmt"
"reflect"
)
func main() {
// Use reflect package to get type information
var i int = 42
var f float64 = 3.14
var s string = "hello"
var b bool = true
fmt.Printf("i type: %T, value: %v\n", i, i)
fmt.Printf("f type: %T, value: %v\n", f, f)
fmt.Printf("s type: %T, value: %v\n", s, s)
fmt.Printf("b type: %T, value: %v\n", b, b)
// Use reflect package
fmt.Printf("i reflection type: %v\n", reflect.TypeOf(i))
fmt.Printf("f reflection type: %v\n", reflect.TypeOf(f))
// Check type kind
t := reflect.TypeOf(i)
fmt.Printf("i kind: %v\n", t.Kind())
}🏷️ Custom Types
Type Definition
go
// Define new types
type MyInt int
type MyString string
type Celsius float64 // Celsius
type Fahrenheit float64 // Fahrenheit
func main() {
var temp1 Celsius = 25.0
var temp2 Fahrenheit = 77.0
fmt.Printf("Celsius: %.1f°C\n", temp1)
fmt.Printf("Fahrenheit: %.1f°F\n", temp2)
// Custom types need explicit conversion
var regular float64 = float64(temp1)
fmt.Printf("Converted to float64: %.1f\n", regular)
// Different custom types also need conversion
// temp1 = temp2 // Error! Different types
temp1 = Celsius((temp2 - 32) * 5 / 9) // Fahrenheit to Celsius
fmt.Printf("Converted Celsius: %.1f°C\n", temp1)
}
// Add methods for custom types
func (c Celsius) ToFahrenheit() Fahrenheit {
return Fahrenheit(c*9/5 + 32)
}
func (f Fahrenheit) ToCelsius() Celsius {
return Celsius((f - 32) * 5 / 9)
}Type Alias
go
// Type alias (Go 1.9+)
type MyInt2 = int // MyInt2 is an alias for int
func main() {
var a int = 10
var b MyInt2 = 20
// Type alias can be assigned directly without conversion
a = b // Correct
b = a // Correct
fmt.Printf("a: %d, b: %d\n", a, b)
fmt.Printf("a type: %T\n", a)
fmt.Printf("b type: %T\n", b) // Shows as int, not MyInt2
}🎯 Type Usage Best Practices
Type Selection Guide
go
func main() {
// 1. Integer type selection
var count int = 100 // Use int in general
var age uint8 = 25 // Use uint8 when range is small and non-negative
var id int64 = 1234567890 // Use int64 when large range is needed
// 2. Floating-point type selection
var price float64 = 99.99 // Use float64 in general (higher precision)
var ratio float32 = 0.5 // Use float32 in memory-sensitive scenarios
// 3. String vs character
var name string = "张三" // Use string for text
var initial rune = '张' // Use rune for single character
// 4. Boolean type
var isActive bool = true // Use bool for flag status
fmt.Printf("Count: %d, Age: %d, ID: %d\n", count, age, id)
fmt.Printf("Price: %.2f, Ratio: %.1f\n", price, ratio)
fmt.Printf("Name: %s, Initial: %c\n", name, initial)
fmt.Printf("Status: %t\n", isActive)
}Type Safety Example
go
// Use types to enhance code safety
type UserID int
type ProductID int
func GetUser(id UserID) string {
return fmt.Sprintf("User%d", id)
}
func GetProduct(id ProductID) string {
return fmt.Sprintf("Product%d", id)
}
func main() {
var userID UserID = 123
var productID ProductID = 456
fmt.Println(GetUser(userID)) // Correct
fmt.Println(GetProduct(productID)) // Correct
// The following calls will cause compilation errors, enhancing type safety
// fmt.Println(GetUser(productID)) // Error! Type mismatch
// fmt.Println(GetProduct(userID)) // Error! Type mismatch
// Need explicit conversion
fmt.Println(GetUser(UserID(productID))) // Correct after explicit conversion
}🎓 Summary
In this chapter, we learned Go's type system in detail:
- ✅ Numeric Types: Detailed usage of integers, floating-point, complex numbers
- ✅ String Type: String operations and character handling
- ✅ Boolean Type: Usage of logical values
- ✅ Zero Value Concept: Understanding default values of types
- ✅ Type Conversion: Explicit conversion and type assertion
- ✅ Custom Types: Type definition and type aliases
- ✅ Best Practices: Type selection and type safety
Understanding Go's type system is the foundation of writing high-quality Go code. Go's strong type system helps us find errors at compile time and improves code reliability.
Next, we will learn Go Variables to deeply understand variable declaration, initialization, and usage.
Type Usage Suggestions
- Prefer basic types like
int,float64,string,bool - Choose specific types when specific size or range is needed
- Use custom types to enhance code readability and type safety
- Note the difference between byte length and character length of strings