1. go
  2. /basics
  3. /type-conversions

Understanding Type Conversions in Go Programming

Type conversion in Go is the process of converting values from one type to another. This guide covers all aspects of type conversion and best practices for safe type handling.

Basic Type Conversions

Numeric Conversions

// Integer conversions
i := 42
var f float64 = float64(i)    // int to float64
var u uint = uint(i)          // int to uint

// Float conversions
f = 3.14
i = int(f)                    // float64 to int (truncates)

// Complex number conversions
c := complex(3.0, 4.0)        // float64 to complex128
r := real(c)                  // get real part
im := imag(c)                // get imaginary part

String Conversions

// Integer to string
i := 42
s := strconv.Itoa(i)          // int to string
s = fmt.Sprintf("%d", i)      // alternative method

// String to integer
i, err := strconv.Atoi("42")  // string to int
if err != nil {
    // handle error
}

// String to other numeric types
f, err := strconv.ParseFloat("3.14", 64)
i64, err := strconv.ParseInt("42", 10, 64)
u64, err := strconv.ParseUint("42", 10, 64)

Type Assertions

Basic Type Assertions

var i interface{} = "hello"

// Type assertion
s, ok := i.(string)
if ok {
    fmt.Println(s)  // "hello"
} else {
    fmt.Println("not a string")
}

// Direct assertion (panics if wrong type)
s = i.(string)

Type Switches

func handleValue(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Printf("Integer: %d\n", v)
    case string:
        fmt.Printf("String: %s\n", v)
    case bool:
        fmt.Printf("Boolean: %v\n", v)
    default:
        fmt.Printf("Unknown type: %T\n", v)
    }
}

Interface Conversions

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

// Converting between interfaces
var r Reader = os.Stdin
w, ok := r.(Writer)
if ok {
    // r also implements Writer
}

Best Practices

1. Safe Conversions

// Good: Safe integer conversion
func safeIntToUint(i int) (uint, error) {
    if i < 0 {
        return 0, fmt.Errorf("negative value: %d", i)
    }
    return uint(i), nil
}

// Good: Safe string to number conversion
func safeStringToInt(s string) (int, error) {
    return strconv.Atoi(s)
}

2. Type Assertion Safety

// Good: Safe type assertion
func processValue(v interface{}) {
    if str, ok := v.(string); ok {
        // Process string
        fmt.Println("String:", str)
        return
    }
    // Handle other types
}

// Avoid: Unsafe type assertion
func unsafeProcess(v interface{}) string {
    return v.(string)  // Panics if v is not string
}

3. Error Handling

// Good: Handle conversion errors
func convertString(s string) (int, error) {
    i, err := strconv.Atoi(s)
    if err != nil {
        return 0, fmt.Errorf("failed to convert %q: %w", s, err)
    }
    return i, nil
}

Common Patterns

1. Custom Type Conversions

type Celsius float64
type Fahrenheit float64

// Conversion methods
func (c Celsius) ToFahrenheit() Fahrenheit {
    return Fahrenheit(c*9/5 + 32)
}

func (f Fahrenheit) ToCelsius() Celsius {
    return Celsius((f - 32) * 5 / 9)
}

2. Generic Type Conversions

// Generic conversion function
func convert[T, U any](value T) (U, error) {
    var result U
    
    // Convert using reflection or type assertion
    // Implementation depends on specific types
    
    return result, nil
}

3. Batch Conversions

// Convert slice of strings to integers
func stringsToInts(strings []string) ([]int, error) {
    ints := make([]int, len(strings))
    for i, s := range strings {
        n, err := strconv.Atoi(s)
        if err != nil {
            return nil, fmt.Errorf("invalid integer at index %d: %w", i, err)
        }
        ints[i] = n
    }
    return ints, nil
}

Performance Considerations

1. Avoid Unnecessary Conversions

// Bad: Unnecessary conversions
func badExample() {
    i := 42
    f := float64(i)
    i = int(f)  // Unnecessary round trip
}

// Good: Keep original type when possible
func goodExample() {
    i := 42
    // Use i directly if float64 not needed
}

2. String Conversions

// Expensive: Multiple string conversions
s := strconv.Itoa(i) + strconv.Itoa(j)

// Better: Use fmt.Sprintf
s := fmt.Sprintf("%d%d", i, j)

// Best: Use strings.Builder
var b strings.Builder
b.WriteString(strconv.Itoa(i))
b.WriteString(strconv.Itoa(j))
s := b.String()

Common Mistakes

1. Loss of Precision

// Wrong: Possible loss of precision
f := 3.99
i := int(f)  // i = 3 (truncated)

// Right: Round if needed
i = int(math.Round(f))  // i = 4

2. Range Overflow

// Wrong: Possible overflow
i := 1000000
b := byte(i)  // Possible overflow

// Right: Check range
if i >= 0 && i <= 255 {
    b := byte(i)
} else {
    // Handle error
}

3. Nil Interface Conversions

// Wrong: Converting nil interface
var i interface{}
s := i.(string)  // Panics

// Right: Check for nil
if i != nil {
    if s, ok := i.(string); ok {
        // Use s
    }
}

Next Steps

  1. Learn about error handling
  2. Explore interfaces
  3. Study reflection
  4. Practice with type embedding

Additional Resources