【问题标题】:Unsafe conversion between identical struct types where only tags differ只有标签不同的相同结构类型之间的不安全转换
【发布时间】:2016-01-05 17:55:30
【问题描述】:

考虑两种结构相同但标签不同的类型:

type Foo struct {
  Id int64 `json:"-"`
}

type Bar struct {
  Id int64 `json:"id"`
}

不幸的是,当两种类型的标签不同时,Go 的成语禁止在两种类型之间进行转换,这是有充分理由的。但是我仍然需要能够控制序列化为 JSON 的数据并且不想使用interface{} 类型。

我的问题是,使用 golang 的 unsafe.Pointer 在结构相同(但不是标签)的两种类型之间执行强制转换有多安全?比如:

rf := &Foo{1}
rb := (*Bar)(unsafe.Pointer(rf))

是否有任何可能引发恐慌,可能是因为在内部,由于标签不同,两种类型中的每一种的数据都略有不同是与实际类型数据和每个类型中的数据分开保存的标签信息在结构上是否相同?

编辑

为了澄清,我应该提一下,尽管上面提供的示例使用了单字段结构,但问题实际上是关于包含多个字段的结构类型。

【问题讨论】:

  • 为什么不手动(逐个字段)或通过反射字段将 struct Foo 复制到 struct Bar 中?标签不同的原因是什么?
  • @elithrar:我试图避免手动复制和使用 interface{} 或反射。标签不同,因为我正在处理的应用程序具有不同的用户类型,并且可能某些结构字段不应该对所有用户类型都可用。
  • @miguelg 如果它们真的相同,我想没关系,因为标签被保存为类型信息的一部分,而不是对象的数据。当然,它是不安全的 :)
  • 如果某些结构字段不适用,请根据需要选择性地将第二个结构嵌入到您的用户结构中。使用 Marshal/Unmarshal 逻辑或 omitempty 标签来选择在离开时如何省略该结构 nil
  • @Not_a_Golfer 我认为unsafe 的意思是“程序员保证安全”而不是“实际上不安全”。例如,通过 unsafe 将 f64 转换为 uint64 的位在任何合理的语言实现中实际上都不安全。这就是为什么math 这样做的原因。当然,有时它实际上是不安全的,因为它依赖于短暂的操作系统或编译器细节,但这只是因为unsafe 允许你做潜在的不安全事情,并不是说用 unsafe 做的任何事情本质上都是坏事。

标签: go


【解决方案1】:

严格来说,这并不安全。原因是spec 的相关部分没有为结构的内存布局提供任何指导。它不保证内存中的字段排序、打包或对齐。理论上,编译器可以根据优化信息,决定两个看似相同的结构根据它们的用法以不同的方式表示。这甚至可能是一个 Heisenbug,在 go test 构建中不会发生有问题的优化。

实际上说,这在任何真正的编译器中都不太可能发生,您可能可以安全地做到这一点。对于像您提供的字段结构这样的字段结构尤其如此。不过,在执行此操作之前,您可能应该通过分析确保复制是不够的。

【讨论】:

    【解决方案2】:

    不幸的是,Go 的成语禁止在两种类型之间进行转换,因为它们的标签不同并且有充分的理由

    release notes for Go 1.8(目前处于测试阶段)似乎表明此限制现已解除:

    语言变化

    当显式地将一个值从一种结构类型转换为另一种时,从 Go 1.8 开始,标签将被忽略。
    因此,两个仅在标签上不同的结构可以从一个转换为另一个

    func example() {
        type T1 struct {
            X int `json:"foo"`
        }
        type T2 struct {
            X int `json:"bar"`
        }
        var v1 T1
        var v2 T2
        v1 = T1(v2) // now legal
    }
    

    【讨论】:

      猜你喜欢
      • 2018-01-09
      • 1970-01-01
      • 1970-01-01
      • 2017-03-19
      • 1970-01-01
      • 1970-01-01
      • 2017-05-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多