Golang中为什么指针类型(如*S)不被视为定义类型?

Golang中为什么指针类型(如*S)不被视为定义类型? 我想知道为什么 *S 不是一个已定义类型,而 S 是。请看这个例子:

package main
import "fmt"

type S struct {}
type SType S // SType 和 S 不能互换使用。
type SPtrType *S // SPtrType 和 *S 可以互换使用,因为 *S 不是一个已定义类型。

func funcPtrS(ptrS *S) { fmt.Printf("funcPtrS's parameter has type: %T \n", ptrS) }
func funcSPtrType(sPtrType SPtrType) { fmt.Printf("funcSPtrType's parameter has type: %T \n", sPtrType) }

func main() {
	var s S = S{}
	fmt.Printf("s has type: %T \n", s)
	var sType SType = SType(s) // 需要强制转换,因为 S 和 SType 都是已定义类型。
	fmt.Printf("sType has type: %T \n", sType)
	var ptrS *S = &s
	fmt.Printf("ptrS has type: %T \n", ptrS)
	var sPtrType SPtrType = ptrS // 不需要强制转换。*S 不是一个已定义类型。
	fmt.Printf("sPtrType has type: %T \n", sPtrType)
	funcPtrS(ptrS)
	funcPtrS(sPtrType) // sPtrType 可以赋值给 *S,因为 *S 不是一个已定义类型。
	funcSPtrType(ptrS) // ptrS 可以赋值给 SPtrType,因为 *S 不是一个已定义类型。
	funcSPtrType(sPtrType)
}

/*
输出:
s has type: main.S
sType has type: main.SType
ptrS has type: *main.S
sPtrType has type: main.SPtrType
funcPtrS's parameter has type: *main.S
funcPtrS's parameter has type: *main.S
funcSPtrType's parameter has type: main.SPtrType
funcSPtrType's parameter has type: main.SPtrType
*/

这些文档 指出:

如果满足以下条件之一,则值 x 可被赋值给类型为 T 的变量("x 可赋值给 T"):

  • x 的类型与 T 相同。
  • x 的类型 VT 具有相同的基础类型,并且 VT 中至少有一个不是已定义类型。

因此,我不能将类型为 main.S 的变量赋值给类型为 main.SType 的变量,因为这两种类型都是已定义类型。我可以将类型为 *main.S 的变量赋值给类型为 main.SPtrType 的变量,反之亦然,无需强制转换,因为 *main.S 不是一个已定义类型。

我不理解的是为什么 main.S 是一个已定义类型,而 *main.S 不是。

GitHub 问题:https://github.com/golang/go/issues/38890


更多关于Golang中为什么指针类型(如*S)不被视为定义类型?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

我明白了。S 是一个已定义类型,但 struct {} 不是一个已定义类型,就像 SPtrType 是一个已定义类型但 *S 不是一个已定义类型一样。你不能将 S 赋值给 SType 的原因是它们两者都是已定义类型。你可以将 *S 赋值给 SPtrType 的原因是这两个类型中只有一个是已定义类型(类型 SPtrType)。

以下是一个示例来说明这一点:

package main
import "fmt"

type S struct {A int} // 可以与 struct {A int} 互换使用

func func1(s1 struct {A int}) { fmt.Printf("func1的参数类型为: %T \n", s1) }
func func2(s2 S) { fmt.Printf("func2的参数类型为: %T \n", s2) }

func main() {
	var s1 struct {A int} = struct {A int}{A: 1}
	fmt.Printf("s1的类型为: %T \n", s1)
	var s2 S = struct {A int}{A: 2}
	fmt.Printf("s2的类型为: %T \n", s2)
	func1(s1)
	func1(s2) // 无需类型转换,因为 struct {A int} 不是一个已定义类型。
	func2(s1) // 无需类型转换,因为 struct {A int} 不是一个已定义类型。
	func2(s2)
}
/*
输出:
s1的类型为: struct { A int } 
s2的类型为: main.S 
func1的参数类型为: struct { A int } 
func1的参数类型为: struct { A int } 
func2的参数类型为: main.S 
func2的参数类型为: main.S 
*/

在上面的例子中,struct {A int} 不是一个已定义类型,但 S 是一个已定义类型,因此我可以在它们之间相互赋值而无需进行类型转换。

更多关于Golang中为什么指针类型(如*S)不被视为定义类型?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言规范中,定义类型(defined type) 是通过类型声明(type关键字)创建的类型。S是通过type S struct{}声明的定义类型,而*S是类型字面量(type literal),不是定义类型。

根据规范,*S指针类型字面量,它由现有的类型S派生而来,但没有独立的类型标识。SPtrType是通过type SPtrType *S创建的定义类型,它和*S共享相同的基础类型(underlying type)*S

由于赋值规则允许在至少一个类型不是定义类型时进行隐式转换,*SSPtrType可以互换赋值。以下是示例:

package main

import "fmt"

type S struct{ X int }
type SPtrType *S

func main() {
    var s S = S{X: 1}
    var p1 *S = &s          // p1 的类型是 *S(类型字面量)
    var p2 SPtrType = &s    // p2 的类型是 SPtrType(定义类型)

    // 隐式赋值:*S 不是定义类型,SPtrType 是定义类型
    p1 = p2  // 允许
    p2 = p1  // 允许

    // 类型检查
    fmt.Printf("p1 type: %T\n", p1) // *main.S
    fmt.Printf("p2 type: %T\n", p2) // main.SPtrType

    // 通过指针修改值
    p1.X = 2
    fmt.Println(s.X) // 输出 2
    p2.X = 3
    fmt.Println(s.X) // 输出 3
}

输出:

p1 type: *main.S
p2 type: main.SPtrType
2
3

关键点:

  • *S是类型字面量,不是定义类型。
  • SPtrType是定义类型,基础类型为*S
  • 赋值兼容性基于规范中的规则:当至少一个类型不是定义类型时,相同基础类型的值可以互换。
回到顶部