Why are pointer types (ex. *S) not defined types?

I’m wondering why *S is not a defined type but S is. Take this example:

package main
import "fmt"

type S struct {}
type SType S // SType and S CANNOT be used interchangeably.
type SPtrType *S // SPtrType and *S CAN be used interchangeably because *S is not a defined type.

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) // Cast necessary because both S and SType are defined types.
	fmt.Printf("sType has type: %T \n", sType)
	var ptrS *S = &s
	fmt.Printf("ptrS has type: %T \n", ptrS)
	var sPtrType SPtrType = ptrS // No cast necessary. *S is not a defined type.
	fmt.Printf("sPtrType has type: %T \n", sPtrType)
	funcPtrS(ptrS)
	funcPtrS(sPtrType) // sPtrType is assignable to *S because *S is not a defined type.
	funcSPtrType(ptrS) // ptrS is assignable to SPtrType because *S is not a defined type.
	funcSPtrType(sPtrType)
}

/*
Output:
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
*/

These docs say:

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 .
  • x 's type V and T have identical underlying types and at least one of V or T is not a defined type.

Thus, I can’t assign a variable of type main.S to a variable of type main.SType because those two types are both defined types. I can assign a variable of type *main.S to a variable of type main.SPtrType and vice versa without casting because *main.S is not a defined type.

What I don’t understand is why main.S is a defined type and *main.S isn’t.

GitHub issue: https://github.com/golang/go/issues/38890

I figured it out. S is a defined type but struct {} is not a defined type, just like SPtrType is a defined type but *S is not a defined type. The reason you can’t assign an S to an SType is because both of them are defined types. The reason you can assign an *S to an SPtrType is because only one of those two types (type SPtrType) is a defined type.

Here is an example to get the point across:

package main
import "fmt"

type S struct {A int} // Can be used interchangeably with struct {A int}

func func1(s1 struct {A int}) { fmt.Printf("func1's parameter has type: %T \n", s1) }
func func2(s2 S) { fmt.Printf("func2's parameter has type: %T \n", s2) }

func main() {
	var s1 struct {A int} = struct {A int}{A: 1}
	fmt.Printf("s1 has type: %T \n", s1)
	var s2 S = struct {A int}{A: 2}
	fmt.Printf("s2 has type: %T \n", s2)
	func1(s1)
	func1(s2) // No casting necessary because struct {A int} is not a defined type.
	func2(s1) // No casting necessary because struct {A int} is not a defined type.
	func2(s2)
}
/*
Output:
s1 has type: struct { A int } 
s2 has type: main.S 
func1's parameter has type: struct { A int } 
func1's parameter has type: struct { A int } 
func2's parameter has type: main.S 
func2's parameter has type: main.S 
*/

In the above example, struct {A int} is not a defined type, but S is a defined type, so I can assign them to one another without casting.

1 Like