英文:
Golang struct assignability and type identity
问题
我有以下两个结构体:
func main() {
type A struct {
v int
}
type B struct {
v int
}
var b B = A{}
}
赋值语句var b B = A{}
会失败,并显示错误信息:
无法将类型为'A{}'的A用作类型B
但是在golang规范:类型标识中,它说:
如果两个结构体类型具有相同的字段顺序,并且相应的字段具有相同的名称、相同的类型和相同的标签,则它们是相同的。来自不同包的非导出字段名称始终是不同的。
所以,我期望类型A
和B
是相同的。在规范:可赋值性中,它说:
如果满足以下条件之一,则值x可以赋值给类型为T的变量("x可以赋值给T"):
- x的类型与T相同。
根据所有这些信息,我倾向于认为赋值应该成功,因为A{}
的类型与B
是相同的,但显然我漏掉了一些东西。
那么,我在理解错误消息的根本原因方面漏掉了什么?
英文:
I have the following 2 structs:
func main() {
type A struct {
v int
}
type B struct {
v int
}
var b B = A{}
}
The assignment var b B = A{}
fails with an error message:
> Cannot use 'A{}' (type A) as the type B
But in the golang spec:Type identity it says:
> Two struct types are identical if they have the same sequence of fields, and if corresponding fields have the same names, and identical types, and identical tags. Non-exported field names from different packages are always different.
so, I expect types A
and B
to be identical. And in the spec:Assignability it says:
> A value x is assignable to a variable of type T ("x is assignable to T") if one of the following conditions applies:
> - x's type is identical to T.
given all that information, I tend to think that the assignment should be succeeded since the type of A{}
is identical to B
, but apparently I'm missing something.
So, what am I missing here to understand the root cause of the error message?
答案1
得分: 5
请注意,你指出的定义以以下方式开始:
定义类型始终与任何其他类型不同。
A
和 B
是定义的类型,它们具有不同的名称,因此它们是不同的。类型 struct { v int }
与任何类型 struct {v int}
相同,但在这种情况下,它们不是定义的类型。例如,你可以声明:
func f(x struct {v int})
然后你可以调用:
f(struct {v int}{v:1})
这两个结构体是相等的。
你仍然可以使用以下方式将 A
和 B
赋值:
var b B = B(A{})
这是因为 A
和 B
的底层类型在结构上是相同的。
英文:
Note that the definition you pointed to starts with:
A defined type is always different from any other type.
A
and B
are defined types, they have different names, so they are different. The type struct { v int }
is identical to any type struct {v int}
, but in this case, these are not defined types. For instance, you can declare:
func f(x struct {v int})
Then you can call:
f(struct {v int}{v:1})
The two structs are equal.
You can still assign A
and B
using:
var b B = B(A{})
This works because the underlying types of A
and B
are structurally identical.
答案2
得分: 3
给定类型 A
和 B
的定义如下:
type A struct {
v int
}
type B struct {
v int
}
这两个类型是否相同?
不相同。根据类型定义:
> 类型定义创建一个新的、独立的类型,其底层类型相同。
A 和 B 的底层类型是什么?
它们都是 struct { v int }
。根据:
> 如果 T
是预声明的布尔、数值或字符串类型,或者是一个类型字面量,那么对应的底层类型就是 T 本身。
因此,根据类型声明的形式 TypeSpec = AliasDecl | TypeDef .
和 TypeDef = identifier Type .
,你可以匹配你的类型并看到 A
和 B
都符合类型定义,其中:
A
和B
是 标识符,以及struct { v int }
是它们的底层结构体 类型字面量。
赋值是如何工作的?
根据可赋值性,你可以看到没有适用的条件:
- x 的类型与 T 相同。 —
A
和B
不相同,因为它们是不同的定义类型 - x 的类型 V 和 T 具有相同的底层类型,并且 V 或 T 中至少有一个不是定义类型。 — 它们都是定义类型
因此,类型为 A
的变量不能赋值给类型为 B
的变量,反之亦然。不可赋值性还意味着你不能将类型为 A
的变量传递给类型为 B
的函数参数,因为:
> 参数必须是可赋值给 F
的参数类型的单值表达式。
Burak 的答案中的示例 func foo(x struct{ v int })
可以编译,因为 foo
的参数类型是一个结构体字面量,而不是定义类型,并且符合可赋值性的条件。
转换是如何工作的?
你可以从 A
转换为 B
,因为根据转换:
> 非常量值 x 可以在以下任何情况下转换为类型 T:
>
> - [...]
> - 忽略结构体标签(见下文),x 的类型和 T 有相同的底层类型。
而我们上面证明了这两个结构体确实具有相同的底层类型。因此,以下代码可以编译通过:
var a A = A(B{})
<hr>
Playground: https://play.golang.org/p/UyEYrbUufA5
英文:
So given types A
and B
defined as:
type A struct {
v int
}
type B struct {
v int
}
Are the types identical?
No. From Type Definitions:
> A type definition creates a new, distinct type with the same underlying type
What are A and B underlying types?
Both are struct { v int }
. From
> If T
is one of the predeclared boolean, numeric, or string types, or a type literal, the corresponding underlying type is T itself.
So given that a type declaration is in the form TypeSpec = AliasDecl | TypeDef .
and TypeDef = identifier Type .
you can pattern-match your types and see that both A
and B
conform to the type definition where:
A
andB
are the identifiers andstruct { v int }
is their underlying struct type literal.
How does assignment work?
From Assignability you can see that no condition applies:
- x's type is identical to T. —
A
andB
are not identical because both are different defined types - x's type V and T have identical underlying types and at least one of V or T is not a defined type. — both are defined types
So a variable of type A
is not assignable to a variable of type B
and vice-versa. Non-assignability also means that you can't pass a variable of type A
to a function parameter of type B
, because:
> arguments must be single-valued expressions assignable to the parameter types of F
The example in Burak's answer with func foo(x struct{ v int })
compiles because foo
's param type is a struct literal, not a defined type, and falls under the conditions for assignability.
How does conversion work?
You can convert from A
to B
because, from Conversion`:
> A non-constant value x can be converted to type T in any of these cases:
>
> - [...]
> - ignoring struct tags (see below), x's type and T have identical underlying types.
And we proved above that both structs do indeed have identical underlying types. Therefore this compiles:
var a A = A(B{})
<hr>
Playground: https://play.golang.org/p/UyEYrbUufA5
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论