Go Type Conversion

Type conversion is an important concept in programming, and Go provides several ways to convert between types. This chapter covers explicit type conversion, type assertions, string conversion, and more.

📋 Type Conversion Basics

Explicit Type Conversion

Go does not support implicit type conversion; all conversions must be explicit.

package main

import "fmt"

func main() {
    // 数值类型转换
    var i int = 42
    var f float64 = float64(i)  // int 转 float64
    var u uint = uint(i)        // int 转 uint
    
    fmt.Printf("int: %d, float64: %.2f, uint: %d\n", i, f, u)
    
    // 精度可能丢失的转换
    var bigFloat float64 = 123.456
    var smallInt int = int(bigFloat)  // 小数部分被截断
    
    fmt.Printf("float64: %.3f -> int: %d\n", bigFloat, smallInt)
    
    // 不同大小整数之间的转换
    var big int64 = 1000000
    var small int32 = int32(big)
    
    fmt.Printf("int64: %d -> int32: %d\n", big, small)
    
    // 字符和数字的转换
    var char rune = 'A'
    var ascii int = int(char)
    var backToChar rune = rune(ascii)
    
    fmt.Printf("字符: %c, ASCII: %d, 转回字符: %c\n", char, ascii, backToChar)
}

Type Conversion Rules

package main

import "fmt"

func main() {
    fmt.Println("=== 数值类型转换规则 ===")
    
    // 整数类型之间的转换
    var a int8 = 127
    var b int16 = int16(a)
    var c int32 = int32(b)
    var d int64 = int64(c)
    
    fmt.Printf("int8(%d) -> int16(%d) -> int32(%d) -> int64(%d)\n", a, b, c, d)
    
    // 浮点数精度转换
    var f64 float64 = 3.141592653589793
    var f32 float32 = float32(f64)  // 精度降低
    var backF64 float64 = float64(f32)
    
    fmt.Printf("原始 float64: %.15f\n", f64)
    fmt.Printf("转为 float32: %.15f\n", f32)
    fmt.Printf("转回 float64: %.15f\n", backF64)
    
    // 有符号和无符号转换
    var signed int = -42
    var unsigned uint = uint(signed)  // 可能产生意外结果
    
    fmt.Printf("有符号: %d -> 无符号: %d\n", signed, unsigned)
    
    // 布尔值转换(需要手动处理)
    var flag bool = true
    var intFlag int
    if flag {
        intFlag = 1
    } else {
        intFlag = 0
    }
    
    fmt.Printf("布尔值: %v -> 整数: %d\n", flag, intFlag)
}

🔤 String Type Conversion

Using the strconv Package

package main

import (
    "fmt"
    "strconv"
)

func main() {
    fmt.Println("=== 字符串与数字转换 ===")
    
    // 字符串转整数
    str1 := "123"
    if num1, err := strconv.Atoi(str1); err == nil {
        fmt.Printf("字符串 '%s' -> 整数: %d\n", str1, num1)
    } else {
        fmt.Printf("转换失败: %v\n", err)
    }
    
    // 整数转字符串
    num2 := 456
    str2 := strconv.Itoa(num2)
    fmt.Printf("整数 %d -> 字符串: '%s'\n", num2, str2)
    
    // 字符串转浮点数
    str3 := "3.14159"
    if float1, err := strconv.ParseFloat(str3, 64); err == nil {
        fmt.Printf("字符串 '%s' -> 浮点数: %.5f\n", str3, float1)
    }
    
    // 浮点数转字符串
    float2 := 2.71828
    str4 := strconv.FormatFloat(float2, 'f', 5, 64)
    fmt.Printf("浮点数 %.5f -> 字符串: '%s'\n", float2, str4)
    
    // 字符串转布尔值
    str5 := "true"
    if bool1, err := strconv.ParseBool(str5); err == nil {
        fmt.Printf("字符串 '%s' -> 布尔值: %v\n", str5, bool1)
    }
    
    // 布尔值转字符串
    bool2 := false
    str6 := strconv.FormatBool(bool2)
    fmt.Printf("布尔值 %v -> 字符串: '%s'\n", bool2, str6)
}

Base Conversion

package main

import (
    "fmt"
    "strconv"
)

func main() {
    fmt.Println("=== 进制转换 ===")
    
    num := 255
    
    // 十进制转其他进制
    binary := strconv.FormatInt(int64(num), 2)   // 二进制
    octal := strconv.FormatInt(int64(num), 8)    // 八进制
    hex := strconv.FormatInt(int64(num), 16)     // 十六进制
    
    fmt.Printf("十进制 %d:\n", num)
    fmt.Printf("  二进制: %s\n", binary)
    fmt.Printf("  八进制: %s\n", octal)
    fmt.Printf("  十六进制: %s\n", hex)
    
    // 其他进制转十进制
    fmt.Println("\n转换回十进制:")
    
    if val1, err := strconv.ParseInt(binary, 2, 64); err == nil {
        fmt.Printf("二进制 '%s' -> 十进制: %d\n", binary, val1)
    }
    
    if val2, err := strconv.ParseInt(octal, 8, 64); err == nil {
        fmt.Printf("八进制 '%s' -> 十进制: %d\n", octal, val2)
    }
    
    if val3, err := strconv.ParseInt(hex, 16, 64); err == nil {
        fmt.Printf("十六进制 '%s' -> 十进制: %d\n", hex, val3)
    }
    
    // 任意进制转换
    fmt.Println("\n任意进制转换:")
    base36 := strconv.FormatInt(int64(num), 36)  // 36进制
    fmt.Printf("十进制 %d -> 36进制: %s\n", num, base36)
    
    if val4, err := strconv.ParseInt(base36, 36, 64); err == nil {
        fmt.Printf("36进制 '%s' -> 十进制: %d\n", base36, val4)
    }
}

🎯 Interface Type Conversion

Type Assertions

package main

import "fmt"

func main() {
    fmt.Println("=== 类型断言 ===")
    
    // 创建接口变量
    var i interface{} = "Hello, World!"
    
    // 基本类型断言
    if str, ok := i.(string); ok {
        fmt.Printf("断言成功: %s (长度: %d)\n", str, len(str))
    } else {
        fmt.Println("断言失败: 不是字符串类型")
    }
    
    // 类型断言失败的情况
    if num, ok := i.(int); ok {
        fmt.Printf("是整数: %d\n", num)
    } else {
        fmt.Println("不是整数类型")
    }
    
    // 不安全的类型断言(可能panic)
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("捕获panic: %v\n", r)
        }
    }()
    
    // 这会触发panic,因为i不是int类型
    // num := i.(int)  // 取消注释会panic
    
    fmt.Println("=== 多种类型处理 ===")
    
    // 处理多种可能的类型
    values := []interface{}{
        42,
        "Hello",
        3.14,
        true,
        []int{1, 2, 3},
    }
    
    for i, value := range values {
        fmt.Printf("值 %d: ", i+1)
        
        switch v := value.(type) {
        case int:
            fmt.Printf("整数: %d\n", v)
        case string:
            fmt.Printf("字符串: %s\n", v)
        case float64:
            fmt.Printf("浮点数: %.2f\n", v)
        case bool:
            fmt.Printf("布尔值: %v\n", v)
        case []int:
            fmt.Printf("整数切片: %v\n", v)
        default:
            fmt.Printf("未知类型: %T\n", v)
        }
    }
}

Interface Conversion Examples

package main

import "fmt"

// 定义接口
type Shape interface {
    Area() float64
}

type Drawable interface {
    Draw()
}

type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return 3.14159 * c.Radius * c.Radius
}

func (c Circle) Draw() {
    fmt.Printf("绘制半径为 %.2f 的圆形\n", c.Radius)
}

type Rectangle struct {
    Width, Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Draw() {
    fmt.Printf("绘制 %.2f x %.2f 的矩形\n", r.Width, r.Height)
}

func main() {
    fmt.Println("=== 接口类型转换 ===")
    
    shapes := []Shape{
        Circle{Radius: 5},
        Rectangle{Width: 4, Height: 3},
    }
    
    for i, shape := range shapes {
        fmt.Printf("形状 %d:\n", i+1)
        fmt.Printf("  面积: %.2f\n", shape.Area())
        
        // 类型断言检查是否实现了Drawable接口
        if drawable, ok := shape.(Drawable); ok {
            fmt.Print("  ")
            drawable.Draw()
        } else {
            fmt.Println("  不支持绘制")
        }
        
        // 使用类型断言获取具体类型
        switch s := shape.(type) {
        case Circle:
            fmt.Printf("  圆形半径: %.2f\n", s.Radius)
        case Rectangle:
            fmt.Printf("  矩形尺寸: %.2f x %.2f\n", s.Width, s.Height)
        }
        
        fmt.Println()
    }
}

🔄 Custom Type Conversion

Defining Conversion Methods

package main

import (
    "fmt"
    "strconv"
    "strings"
)

// 自定义类型
type Temperature float64
type Distance int
type Person struct {
    Name string
    Age  int
}

// 温度转换方法
func (t Temperature) ToCelsius() Temperature {
    return t
}

func (t Temperature) ToFahrenheit() Temperature {
    return t*9/5 + 32
}

func (t Temperature) ToKelvin() Temperature {
    return t + 273.15
}

func (t Temperature) String() string {
    return fmt.Sprintf("%.2f°C", float64(t))
}

// 距离转换方法
func (d Distance) ToMeters() float64 {
    return float64(d)
}

func (d Distance) ToKilometers() float64 {
    return float64(d) / 1000
}

func (d Distance) ToMiles() float64 {
    return float64(d) / 1609.34
}

func (d Distance) String() string {
    return fmt.Sprintf("%d米", int(d))
}

// Person 转换方法
func (p Person) ToString() string {
    return fmt.Sprintf("%s (%d岁)", p.Name, p.Age)
}

func (p Person) ToJSON() string {
    return fmt.Sprintf(`{"name":"%s","age":%d}`, p.Name, p.Age)
}

func PersonFromString(s string) (Person, error) {
    parts := strings.Split(s, ",")
    if len(parts) != 2 {
        return Person{}, fmt.Errorf("格式错误,应为: 姓名,年龄")
    }
    
    name := strings.TrimSpace(parts[0])
    ageStr := strings.TrimSpace(parts[1])
    
    age, err := strconv.Atoi(ageStr)
    if err != nil {
        return Person{}, fmt.Errorf("年龄格式错误: %v", err)
    }
    
    return Person{Name: name, Age: age}, nil
}

func main() {
    fmt.Println("=== 自定义类型转换 ===")
    
    // 温度转换
    temp := Temperature(25)
    fmt.Printf("温度转换:\n")
    fmt.Printf("  摄氏度: %s\n", temp.String())
    fmt.Printf("  华氏度: %.2f°F\n", temp.ToFahrenheit())
    fmt.Printf("  开尔文: %.2f K\n", temp.ToKelvin())
    
    // 距离转换
    distance := Distance(5000)
    fmt.Printf("\n距离转换:\n")
    fmt.Printf("  米: %s\n", distance.String())
    fmt.Printf("  千米: %.2f公里\n", distance.ToKilometers())
    fmt.Printf("  英里: %.2f英里\n", distance.ToMiles())
    
    // Person转换
    person := Person{Name: "张三", Age: 25}
    fmt.Printf("\nPerson转换:\n")
    fmt.Printf("  字符串: %s\n", person.ToString())
    fmt.Printf("  JSON: %s\n", person.ToJSON())
    
    // 从字符串创建Person
    personStr := "李四, 30"
    if p, err := PersonFromString(personStr); err == nil {
        fmt.Printf("  从字符串创建: %s\n", p.ToString())
    } else {
        fmt.Printf("  创建失败: %v\n", err)
    }
}

⚠️ Type Conversion Pitfalls

Precision Loss and Overflow

package main

import (
    "fmt"
    "math"
)

func main() {
    fmt.Println("=== 类型转换陷阱 ===")
    
    // 1. 浮点数精度损失
    fmt.Println("1. 浮点数精度损失:")
    var precise float64 = 1.23456789012345
    var imprecise float32 = float32(precise)
    var backToPrecise float64 = float64(imprecise)
    
    fmt.Printf("原始 float64: %.15f\n", precise)
    fmt.Printf("转为 float32: %.15f\n", imprecise)
    fmt.Printf("转回 float64: %.15f\n", backToPrecise)
    fmt.Printf("精度损失: %.15f\n", precise-backToPrecise)
    
    // 2. 整数溢出
    fmt.Println("\n2. 整数溢出:")
    var big int64 = math.MaxInt32 + 1
    var small int32 = int32(big)  // 溢出
    
    fmt.Printf("原始 int64: %d\n", big)
    fmt.Printf("转为 int32: %d\n", small)
    fmt.Printf("最大 int32: %d\n", math.MaxInt32)
    
    // 3. 有符号无符号转换陷阱
    fmt.Println("\n3. 有符号无符号转换:")
    var negative int = -100
    var positive uint = uint(negative)  // 危险!
    
    fmt.Printf("有符号 int: %d\n", negative)
    fmt.Printf("转为 uint: %d\n", positive)
    
    // 4. 字符串转换失败
    fmt.Println("\n4. 字符串转换错误处理:")
    invalidStrings := []string{"abc", "12.34.56", "", "999999999999999999999"}
    
    for _, s := range invalidStrings {
        if num, err := strconv.Atoi(s); err != nil {
            fmt.Printf("字符串 '%s' 转换失败: %v\n", s, err)
        } else {
            fmt.Printf("字符串 '%s' 转换成功: %d\n", s, num)
        }
    }
    
    // 5. 类型断言失败
    fmt.Println("\n5. 类型断言安全检查:")
    var unknown interface{} = 3.14
    
    // 安全的类型断言
    if str, ok := unknown.(string); ok {
        fmt.Printf("是字符串: %s\n", str)
    } else {
        fmt.Println("不是字符串类型")
    }
    
    // 获取实际类型
    fmt.Printf("实际类型: %T, 值: %v\n", unknown, unknown)
}

🛠️ Practical Conversion Utilities

Generic Conversion Helper Functions

package main

import (
    "fmt"
    "reflect"
    "strconv"
)

// 通用转换工具
type Converter struct{}

// 尝试将任意值转换为字符串
func (c Converter) ToString(value interface{}) string {
    switch v := value.(type) {
    case string:
        return v
    case int, int8, int16, int32, int64:
        return fmt.Sprintf("%d", v)
    case uint, uint8, uint16, uint32, uint64:
        return fmt.Sprintf("%d", v)
    case float32, float64:
        return fmt.Sprintf("%g", v)
    case bool:
        return strconv.FormatBool(v)
    case nil:
        return ""
    default:
        return fmt.Sprintf("%v", v)
    }
}

// 尝试将字符串转换为指定类型
func (c Converter) FromString(s string, targetType reflect.Type) (interface{}, error) {
    switch targetType.Kind() {
    case reflect.String:
        return s, nil
    case reflect.Int:
        return strconv.Atoi(s)
    case reflect.Int64:
        return strconv.ParseInt(s, 10, 64)
    case reflect.Float64:
        return strconv.ParseFloat(s, 64)
    case reflect.Bool:
        return strconv.ParseBool(s)
    default:
        return nil, fmt.Errorf("不支持的类型: %v", targetType)
    }
}

// 检查两个值是否可以进行类型转换
func (c Converter) CanConvert(from, to reflect.Type) bool {
    return from.ConvertibleTo(to)
}

// 安全的类型转换
func (c Converter) SafeConvert(value interface{}, targetType reflect.Type) (interface{}, error) {
    sourceType := reflect.TypeOf(value)
    
    if sourceType == targetType {
        return value, nil
    }
    
    if !sourceType.ConvertibleTo(targetType) {
        return nil, fmt.Errorf("无法从 %v 转换到 %v", sourceType, targetType)
    }
    
    sourceValue := reflect.ValueOf(value)
    convertedValue := sourceValue.Convert(targetType)
    
    return convertedValue.Interface(), nil
}

func main() {
    converter := Converter{}
    
    fmt.Println("=== 通用转换工具测试 ===")
    
    // 测试转换为字符串
    values := []interface{}{
        123,
        3.14159,
        true,
        "hello",
        nil,
        []int{1, 2, 3},
    }
    
    fmt.Println("转换为字符串:")
    for _, value := range values {
        str := converter.ToString(value)
        fmt.Printf("  %T(%v) -> string(%s)\n", value, value, str)
    }
    
    // 测试从字符串转换
    fmt.Println("\n从字符串转换:")
    stringValues := map[string]reflect.Type{
        "123":   reflect.TypeOf(int(0)),
        "3.14":  reflect.TypeOf(float64(0)),
        "true":  reflect.TypeOf(bool(false)),
        "hello": reflect.TypeOf(string("")),
    }
    
    for str, targetType := range stringValues {
        if result, err := converter.FromString(str, targetType); err == nil {
            fmt.Printf("  string(%s) -> %v(%v)\n", str, targetType, result)
        } else {
            fmt.Printf("  string(%s) -> %v 转换失败: %v\n", str, targetType, err)
        }
    }
    
    // 测试安全转换
    fmt.Println("\n安全类型转换:")
    testCases := []struct {
        value  interface{}
        target reflect.Type
    }{
        {int(42), reflect.TypeOf(float64(0))},
        {float64(3.14), reflect.TypeOf(int(0))},
        {true, reflect.TypeOf(int(0))},
        {"hello", reflect.TypeOf(int(0))},
    }
    
    for _, tc := range testCases {
        if result, err := converter.SafeConvert(tc.value, tc.target); err == nil {
            fmt.Printf("  %T(%v) -> %v(%v)\n", tc.value, tc.value, tc.target, result)
        } else {
            fmt.Printf("  %T(%v) -> %v 转换失败: %v\n", tc.value, tc.value, tc.target, err)
        }
    }
}

🎓 Summary

In this chapter, we explored Go type conversion in depth:

  • Explicit conversion: conversions between basic data types
  • String conversion: using the strconv package and base conversion
  • Interface conversion: type assertions and interface type checks
  • Custom conversion: adding conversion methods to custom types
  • Conversion pitfalls: precision loss, overflow, and other common issues
  • Practical utilities: implementing generic conversion helper functions

Understanding and mastering type conversion is essential for writing robust Go code.


Next, we will learn about Go Interfaces, the core feature of object-oriented programming in Go.

::: tip Type Conversion Recommendations

  • Always use explicit type conversion; Go does not support implicit conversion
  • Watch for precision loss and overflow when converting numeric types
  • Check the ok return value when using type assertions
  • Handle errors that may occur during string conversion :::