Golang中数组和切片为何能赋值不同类型而普通变量不行

Golang中数组和切片为何能赋值不同类型而普通变量不行 我定义了三种类型:用于变量的MyType1: int32、用于数组的MyType2: [2]int32和用于切片的MyType3: []int。当我尝试将myType1(7)赋值给变量时,会报错(IncompaibleAssign)。这对我来说是预期的行为,因为myType1类型的常量7不能赋值给int32类型的变量。然而,为数组和切片赋值不同类型却可以正常工作。例如,myType2{20, 40}居然可以赋值给类型为[2]int32的数组。这是怎么实现的?我原本以为需要像var array [2]int32 = [2]int32(myType2{20, 40})这样进行转换,或者将数组类型改为myType2。请不要告诉我这是因为底层类型。我的意思是,如果那样的话,一切对我来说都不再清晰了,因为那样的话为什么变量就不行呢?

package main

func main() {

    type myType1 int32
    var variable int32 = myType1(7)

    type myType2 [2]int32
    var array [2]int32 = myType2{20, 40}

    type myType3 []int32
    var slice []int32 = myType3{20, 40}

    _, _, _ = variable, array, slice
}

更多关于Golang中数组和切片为何能赋值不同类型而普通变量不行的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

感谢您的回复。我认为这是由于“赋值规则”导致的。更具体地说,是关于具有相同底层类型的命名类型和未命名类型之间的赋值规则。@skillian 对此进行了更详细的解释。

更多关于Golang中数组和切片为何能赋值不同类型而普通变量不行的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好,@Grujic_Filip,我认为这里的答案如下。

你试图将一个常量强制赋值给一个你认为表示相同类型的变量。没错,int32myType1 都表示底层类型为 int32,但当你计算一个常量的底层类型时(在此例中是内置类型),Go 语言会给出 myType1 本身,因此返回 IncompatibleAssign。实际上,你可以尝试在生成了 int32myType1 两个不同的常量后,打印它们的类型。这是 Go 语言的一条特定规则:Go 转换与赋值

实际上,如果你写成下面这样,它是可以工作的:

type myType1 int32
var variable = int32(myType1(5))

对于整数切片或数组,它们被视为第一个底层类型,即 int32 指针。更多信息请参阅:Go 类型系统

希望这个解释足够有用 🙂。

有趣的问题!我认为这是因为语言规范中“可赋值性”部分的第二个条件:

  • VT 具有相同的底层类型,但都不是类型参数,并且 VT 中至少有一个不是命名类型

所以,因为 myType2 的底层类型是 [2]int32,而你正在赋值给 [2]int32,这是可以的。但是,如果你有两个不同的命名类型,它们的底层类型都是 [2]int32,这就不行了:

type myType2a [2]int32
type myType2b [2]int32
var array myType2b = myType2a{20, 40}
// 错误:无法将 myType2a{…} (类型为 myType2a 的值) 用作变量声明中的 myType2b 类型

The Go Play Space

在Go语言中,数组和切片能够赋值给不同类型是因为Go的赋值规则对复合类型有特殊处理。根据Go语言规范,当两个类型具有相同的底层类型且至少有一个不是命名类型时,它们可以相互赋值。对于数组,还需要长度相同。

在你的例子中:

  • myType2[2]int32 都是数组类型,具有相同的底层类型 [2]int32,且 [2]int32 是未命名类型。
  • myType3[]int32 都是切片类型,具有相同的底层类型 []int32,且 []int32 是未命名类型。

因此,这些赋值是允许的。而对于基本类型 myType1int32,虽然它们具有相同的底层类型,但两者都是命名类型,所以不能直接赋值。

以下是代码示例,展示了允许和不允许的情况:

package main

func main() {
    // 不允许:两个都是命名类型
    type myType1 int32
    var variable int32 = myType1(7) // 编译错误

    // 允许:一个是命名类型,另一个是未命名类型
    type myType2 [2]int32
    var array [2]int32 = myType2{20, 40} // 正确

    // 允许:一个是命名类型,另一个是未命名类型
    type myType3 []int32
    var slice []int32 = myType3{20, 40} // 正确

    // 不允许:两个都是命名类型
    type myType4 []int32
    type myType5 []int32
    var slice2 myType5 = myType4{1, 2} // 编译错误
}

这种设计允许在保持类型安全的同时,为复合类型提供一定的灵活性。

回到顶部