结构体转换怎么才算优雅

253 阅读1分钟

问题引入

在不同的包下定义了一些结构体 其所有字段都相同(内存布局完全一致),但是他们却属于不同的结构体

所以经常需要在这些结构体之间来回转换,主要实现思路有:

  • 手写转换函数 逐个字段的赋值 字段很多的结构体书写费时费力

  • 借助代码生成工具生成转换函数 本质上还是会New一个新的结构体

  • 先对结构体UnMarshalMarshal的方式完成转换 效率不高

  • 深拷贝 使用copier等库

  • 原地类型强转 不涉及内存分配

举例

以下主要记录 原地类型转换的实现

// 来自 pkg f
type A struct {
    B string
    C int64
    D string
}

// 来自 pkg g
type A struct {
    B string
    C int64
    D string
}

实现一

针对同一片内存空间做类型转换

Unsafe概念 见 必知必会系列-Unsafe

func convert_1(a *f.A) *g.A {
    return (*g.A)(*(*unsafe.Pointer)(unsafe.Pointer(&a)))
}

实现二

使用反射做转换

反射/接口原理 见 必知必会系列-interface

var (
    gAType = reflect.TypeOf(g.A{})
)

func convert_2(a *f.A) *g.A {
    return reflect.NewAt(gAType, unsafe.Pointer(a)).Interface().(*g.A)
}

注意点

上述的转换很高效,但是如果转换前后的结构体内存布局不一致,可能就会出现一些诡异的情况。

在真实使用过程中 需要确保两个结构体内存布局一致