Understanding Basic Types in Go: A Comprehensive Guide

Intro

Go provides a rich set of basic types that form the foundation of its type system. Let's explore each category in detail with practical examples.

Numeric Types

Integers

Go offers both signed and unsigned integers of various sizes:

// Signed integers
var (
    num8  int8  = 127         // -128 to 127
    num16 int16 = 32767       // -32768 to 32767
    num32 int32 = 2147483647  // -2147483648 to 2147483647
    num64 int64 = 9223372036854775807
)

// Unsigned integers
var (
    unum8  uint8  = 255              // 0 to 255
    unum16 uint16 = 65535            // 0 to 65535
    unum32 uint32 = 4294967295       // 0 to 4294967295
    unum64 uint64 = 18446744073709551615
)

// Architecture-dependent types
var (
    num  int  = 42    // 32 or 64 bits depending on system
    unum uint = 42    // 32 or 64 bits depending on system
)

Floating-Point Numbers

Go provides two floating-point types:

// Float types
var (
    f32 float32 = 3.14159265359  // Approximately 6 decimal digits of precision
    f64 float64 = 3.14159265359  // Approximately 15 decimal digits of precision
)

// Scientific notation
var (
    avogadro    float64 = 6.02214e23
    plancksConst float64 = 6.62607015e-34
)

Complex Numbers

Go has built-in support for complex numbers:

var (
    c64  complex64  = 3 + 4i   // Complex number with float32 real and imaginary parts
    c128 complex128 = 5 + 7i   // Complex number with float64 real and imaginary parts
)

// Working with complex numbers
func complexDemo() {
    z := complex(3, 4)  // Creating a complex number
    fmt.Printf("z = %v\n", z)
    fmt.Printf("Real part: %f\n", real(z))
    fmt.Printf("Imaginary part: %f\n", imag(z))
    fmt.Printf("Complex conjugate: %v\n", cmplx.Conj(z))
}

Text Types

Strings

Strings in Go are immutable sequences of bytes:

var (
    name     string = "Hello, 世界"    // UTF-8 encoded string
    rawStr   string = `This is a
                      raw string literal
                      that preserves line breaks`
)

func stringDemo() {
    // String operations
    str := "Hello, World!"
    fmt.Printf("Length: %d\n", len(str))
    fmt.Printf("Character at index 0: %c\n", str[0])
    fmt.Printf("Substring: %s\n", str[0:5])

    // String concatenation
    first := "Hello"
    second := "World"
    combined := first + " " + second
    fmt.Println(combined)
}

Runes

Runes represent Unicode code points:

func runeDemo() {
    // Rune literal
    var r rune = '世'
    fmt.Printf("Rune: %c, Unicode: %U, Value: %d\n", r, r, r)

    // Iterating over string runes
    str := "Hello, 世界"
    for i, r := range str {
        fmt.Printf("Position %d: %c\n", i, r)
    }
}

Boolean Type

Go's boolean type can be either true or false:

func booleanDemo() {
    var (
        isActive bool = true
        isValid  bool = false
    )

    // Boolean operators
    fmt.Printf("AND: %v\n", isActive && isValid)
    fmt.Printf("OR: %v\n", isActive || isValid)
    fmt.Printf("NOT: %v\n", !isActive)

    // Conditional statements
    if isActive {
        fmt.Println("Active")
    }
}

Zero Values

Every type in Go has a zero value. Variables declared without an explicit initial value are set to their zero value:

func zeroValues() {
    var (
        i int
        f float64
        b bool
        s string
        r rune
    )

    fmt.Printf("Integer zero value: %d\n", i)       // 0
    fmt.Printf("Float zero value: %f\n", f)         // 0.0
    fmt.Printf("Boolean zero value: %v\n", b)       // false
    fmt.Printf("String zero value: %q\n", s)        // ""
    fmt.Printf("Rune zero value: %d\n", r)         // 0
}

Type Conversions

Go requires explicit type conversions between different types:

func typeConversions() {
    // Numeric conversions
    var i int32 = 100
    var j int64 = int64(i)
    var f float64 = float64(i)

    // String conversions
    var num int = 42
    str := strconv.Itoa(num)      // Integer to string
    num2, err := strconv.Atoi(str) // String to integer

    // Float to string with precision
    pi := 3.14159
    piStr := fmt.Sprintf("%.2f", pi)

    fmt.Printf("j: %d, f: %f, str: %s, num2: %d, piStr: %s\n",
               j, f, str, num2, piStr)
}

Type Inference

Go can infer types using the short declaration syntax:

func typeInference() {
    // Type inference using :=
    name := "John"        // string
    age := 25            // int
    height := 1.75       // float64
    isStudent := true    // bool

    fmt.Printf("Types - name: %T, age: %T, height: %T, isStudent: %T\n",
               name, age, height, isStudent)
}

Best Practices

  1. Use the most appropriate type for your needs:

    • int for general integer values

    • float64 for floating-point calculations

    • string for text

    • rune for Unicode characters

  2. Consider memory usage when choosing numeric types:

    • Use smaller integer types when you know the value range

    • Use float32 only when memory is critical; prefer float64

  3. Be careful with floating-point comparisons:

func floatingPointComparison() {
    a := 0.1
    b := 0.2
    c := 0.3

    fmt.Printf("%.20f + %.20f = %.20f\n", a, b, a+b)
    fmt.Printf("%.20f = %.20f is %v\n", a+b, c, a+b == c)

    // Better comparison using tolerance
    tolerance := 0.0000001
    diff := math.Abs((a + b) - c)
    fmt.Printf("Equal within tolerance: %v\n", diff < tolerance)
}

This overview covers the fundamental types in Go. Each type has its specific use cases and characteristics, and understanding them well is crucial for writing effective Go programs.