Skip to content

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

Content is for learning and research only.