相同结构类型之间的不安全转换,只有标签不同。

huangapple go评论88阅读模式
英文:

Unsafe conversion between identical struct types where only tags differ

问题

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

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

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

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

我的问题是,使用Go语言的unsafe.Pointer在两种在结构上相同但标签不同的类型之间执行类型转换有多安全?类似于以下代码:

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

是否存在任何可能导致恐慌的机会,可能是因为由于标签不同,每种类型中的数据在内部略有不同,或者关于标签的信息与实际类型数据分开保存,而每种类型中的数据在结构上是相同的?

编辑

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

英文:

Consider two types identical in structure but differing in their tags:

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

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

Unfortunately Go's idiom forbids casting between two types when their tags differ and for good reason. However I still need to be able to control the data that is serialised to JSON and don't want to use interface{} types.

My question is, how safe is it to use golang's unsafe.Pointer to perform casts between two types which are identical in structure (but not tags)? Something like:

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

Is there any chance at all of a panic ensuing maybe because internally the data in each of the two types is held slightly differently due to the tags differing or is the information about tags held separate from the actual type data and data in each of the types is structurally identical?

EDIT

For clarification I should mention that although the example provided above employs single-field structs, the question is really about struct types containing multiple fields.

答案1

得分: 1

严格来说,这是不安全的。原因是规范的相关部分没有给出关于结构体内存布局的任何指导方针。它不保证内存中字段的顺序、打包或对齐方式。理论上,编译器可以根据优化信息决定以不同的方式表示两个看似相同的结构体,这可能是一个Heisenbug,其中有害的优化在go test构建中不会发生。

从实际角度来看,在任何真实的编译器中,这种情况不太可能发生,你可能可以安全地这样做。对于像你提供的这样的单字段结构体,你应该在进行此操作之前通过性能分析确保复制是不够的。

英文:

Strictly speaking, this isn't safe. The reason is that the relevant section of the spec doesn't give any guidelines for memory layout for structs. It doesn't guarantee in-memory field ordering, packing, or alignment. In theory, a compiler could, based on optimization info, decide two seemingly identical structs are to be represented differently based on their usage. This could even be a Heisenbug where the offending optimization doesn't happen in go test builds.

Practically speaking, this is unlikely to happen in any real compiler and you can probably do it safely. This is especially true of one field structs like the one you provided. You should probably ensure through profiling that copying is insufficient before you go doing this though.

答案2

得分: 1

很抱歉,Go语言的习惯用法禁止在两个类型的标签不同时进行类型转换,这是有充分理由的。

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) // 现在是合法的
}
英文:

> Unfortunately Go's idiom forbids casting between two types when their tags differ and for good reason

The release notes for Go 1.8 (currently in beta) seems to indicate this restriction is now lifted:

> ## Changes to the language

> When explicitly converting a value from one struct type to another, as of Go 1.8 the tags are ignored.
Thus two structs that differ only in their tags may be converted from one to the other:

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
}

huangapple
  • 本文由 发表于 2015年10月8日 16:13:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/33010220.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定