package main
import "net/http"
func main() {
type P = *bool
type M = map[int]int
var x struct {
string // a named non-pointer type
error // a named interface type
*int // an unnamed pointer type
P // an alias of an unnamed pointer type
M // an alias of an unnamed type
http.Header // a named map type
}
x.string = "Go"
x.error = nil
x.int = new(int)
x.P = new(bool)
x.M = make(M)
x.Header = http.Header{}
}
string
, error
, int
, P
, M
, and Header
, respectively.
T
or as a pointer to a non-interface type name *T
, and T
itself may not be a pointer type.
P
field in the example in the last section.
T
can be embedded as an embedded field unless T
denotes a named pointer type or a pointer type whose base type is either a pointer or an interface type.
*T
, where T
is a type name denoting the base type of the pointer type, can be embedded as an embedded field unless type name T
denotes a pointer or interface type.
type Encoder interface {Encode([]byte) []byte}
type Person struct {name string; age int}
type Alias = struct {name string; age int}
type AliasPtr = *struct {name string; age int}
type IntPtr *int
type AliasPP = *IntPtr
// These types and aliases can be embedded.
Encoder
Person
*Person
Alias
*Alias
AliasPtr
int
*int
// These types and aliases can't be embedded.
AliasPP // base type is a pointer type
*Encoder // base type is an interface type
*AliasPtr // base type is a pointer type
IntPtr // named pointer type
*IntPtr // base type is a pointer type
*chan int // base type is an unmaed type
struct {age int} // unnamed non-pointer type
map[string]int // unnamed non-pointer type
[]int64 // unnamed non-pointer type
func() // unnamed non-pointer type
int
and *int
can't be embedded in the same struct type.
T
inherits another type, then type T
obtains the abilities of the other type. At the same time, each value of type T
can also be viewed as a value of the other type.
T
embeds another type, then type other type becomes a part of type T
, and type T
obtains the abilities of the other type, but none values of type T
can be viewed as values of the other type.
package main
import "fmt"
type Person struct {
Name string
Age int
}
func (p Person) PrintName() {
fmt.Println("Name:", p.Name)
}
func (p *Person) SetAge(age int) {
p.Age = age
}
type Singer struct {
Person // extends Person by embedding it
works []string
}
func main() {
var gaga = Singer{Person: Person{"Gaga", 30}}
gaga.PrintName() // Name: Gaga
gaga.Name = "Lady Gaga"
(&gaga).SetAge(31)
(&gaga).PrintName() // Name: Lady Gaga
fmt.Println(gaga.Age) // 31
}
Person
, the type Singer
obtains all methods and fields of type Person
, and type *Singer
obtains all methods of type *Person
. Are the conclusions right? The following sections will answer this question.
Singer
value is not a Person
value, the following code doesn't compile:
var gaga = Singer{}
var _ Person = gaga
Singer
and the methods of type *Singer
used in the last example by using the reflection functionalities provided in the reflect
standard package.
package main
import (
"fmt"
"reflect"
)
... // the types declared in the last example
func main() {
t := reflect.TypeOf(Singer{}) // the Singer type
fmt.Println(t, "has", t.NumField(), "fields:")
for i := 0; i < t.NumField(); i++ {
fmt.Print(" field#", i, ": ", t.Field(i).Name, "\n")
}
fmt.Println(t, "has", t.NumMethod(), "methods:")
for i := 0; i < t.NumMethod(); i++ {
fmt.Print(" method#", i, ": ", t.Method(i).Name, "\n")
}
pt := reflect.TypeOf(&Singer{}) // the *Singer type
fmt.Println(pt, "has", pt.NumMethod(), "methods:")
for i := 0; i < pt.NumMethod(); i++ {
fmt.Print(" method#", i, ": ", pt.Method(i).Name, "\n")
}
}
main.Singer has 2 fields: field#0: Person field#1: works main.Singer has 1 methods: method#0: PrintName *main.Singer has 2 methods: method#0: PrintName method#1: SetAge
Singer
really owns a PrintName
method, and the type *Singer
really owns two methods, PrintName
and SetAge
. But the type Singer
doesn't own a Name
field. Then why is the selector expression gaga.Name
legal for a Singer
value gaga
? Please read the next section to get the reason.
x
, x.y
is called a selector, where y
is either a field name or a method name. If y
is a field name, then x
must be a struct value or a struct pointer value. A selector is an expression, which represents a value. If the selector x.y
denotes a field, it may also has its own fields (if x.y
is a struct value) and methods. Such as x.y.z
, where z
can also be either a field name or a method name.
package main
type A struct {
FieldX int
}
func (a A) MethodA() {}
type B struct {
*A
}
type C struct {
B
}
func main() {
var c = &C{B: B{A: &A{FieldX: 5}}}
// The following 4 lines are equivalent.
_ = c.B.A.FieldX
_ = c.B.FieldX
_ = c.A.FieldX // A is a promoted field of C
_ = c.FieldX // FieldX is a promoted field
// The following 4 lines are equivalent.
c.B.A.MethodA()
c.B.MethodA()
c.A.MethodA()
c.MethodA() // MethodA is a promoted method of C
}
gaga.Name
is legal in the example in the last section. For it is just the shorthand of gaga.Person.Name
.
gaga.PrintName
can be viewed as a shorthand of gaga.Person.PrintName
. But, it is also okay if we think it is not a shorthand. After all, the type Singer
really has a PrintName
method, though the method is declared implicitly (please read the section after next for details). For the similar reason, the selector (&gaga).PrintName
and (&gaga).SetAge
can also be viewed as, or not as, shorthands of (&gaga.Person).PrintName
and (&gaga.Person).SetAge
.
Name
is called a promoted field of type Singer
. PrintName
is called a promoted method of type Singer
.
gaga.SetAge
, only if gaga
is an addressable value of type Singer
. It is just syntactical sugar of (&gaga).SetAge
. Please read method calls for details.
c.B.A.FieldX
is called the full form of selectors c.FieldX
, c.B.FieldX
and c.A.FieldX
. Similarly, c.B.A.MethodA
is called the full form of selectors c.MethodA
, c.B.MethodA
and c.A.MethodA
.
c.MethodA
used in an above example is 2, for the full form of the selector is c.B.A.MethodA
.
x
(we should always assume it is addressable, even if it is not), it is possible that many of its full-form selectors have the same last item y
and every middle name of these selectors represents an embedded field. For such cases,
x.y
. In other words, x.y
denotes the full-form selector with the shallowest depth. Other full-form selectors are shadowed by the one with the shallowest depth.
x.y
. We say those full-form selectors with the shallowest depth are colliding with each other.
type A struct {
x string
}
func (A) y(int) bool {
return false
}
type B struct {
y bool
}
func (B) x(string) {}
type C struct {
B
}
v1.A.x
and v1.B.x
are equal, so the two selectors collide with each other and neither of them can be shortened to v1.x
. The same situation is for the selectors v1.A.y
and v1.B.y
.
var v1 struct {
A
B
}
func f1() {
_ = v1.x // error: ambiguous selector v1.x
_ = v1.y // error: ambiguous selector v1.y
}
v2.C.B.x
is shadowed by v2.A.x
, so the selector v2.x
is a shortened form of v2.A.x
actually. For the same reason, the selector v2.y
is a shortened form of v2.A.y
, not of v2.C.B.y
.
var v2 struct {
A
C
}
func f2() {
fmt.Printf("%T \n", v2.x) // string
fmt.Printf("%T \n", v2.y) // func(int) bool
}
.M
and .z
selectors still get promoted in the following example.
package main
type x string
func (x) M() {}
type y struct {
z byte
}
type A struct {
x
}
func (A) y(int) bool {
return false
}
type B struct {
y
}
func (B) x(string) {}
func main() {
var v struct {
A
B
}
//_ = v.x // error: ambiguous selector v.x
//_ = v.y // error: ambiguous selector v.y
_ = v.M // ok. <=> v.A.x.M
_ = v.z // ok. <=> v.B.y.z
}
m()
occurrences are replaced with M()
, then the program will fail to compile for A.M
and B.M
collide with each other, so c.M
is not a valid selector.
package foo // import "x.y/foo"
import "fmt"
type A struct {
n int
}
func (a A) m() {
fmt.Println("A", a.n)
}
type I interface {
m()
}
func Bar(i I) {
i.m()
}
package main
import "fmt"
import "x.y/foo"
type B struct {
n bool
}
func (b B) m() {
fmt.Println("B", b.n)
}
type C struct{
foo.A
B
}
func main() {
var c C
c.m() // B false
foo.Bar(c) // A 0
}
Singer
and type *Singer
have a PrintName
method each, and the type *Singer
also has a SetAge
method. However, we never explicitly declare these methods for the two types. Where do these methods come from?
S
embeds a type (or a type alias) T
and the embedding is legal,
T
, if the selectors to that method neither collide with nor are shadowed by other selectors, then compilers will implicitly declare a corresponding method with the same specification for the embedding struct type S
. And consequently, compilers will also implicitly declare a corresponding method for the pointer type *S
.
*T
, if the selectors to that method neither collide with nor are shadowed by other selectors, then compilers will implicitly declare a corresponding method with the same specification for the pointer type *S
.
struct{T}
and type *struct{T}
both obtain all the methods of the type denoted by T
.
*struct{T}
, type struct{*T}
, and type *struct{*T}
all obtain all the methods of type *T
.
Singer
and type *Singer
.
// Note: these declarations are not legal Go syntax.
// They are shown here just for explanation purpose.
// They indicate how implicit method values are
// evaluated (see the next section for more).
func (s Singer) PrintName = s.Person.PrintName
func (s *Singer) PrintName = (*s).Person.PrintName
func (s *Singer) SetAge = (&(*s).Person).SetAge
I
.
Age
has no methods, for it doesn't embed any types.
X
has two methods, IsOdd
and Double
. IsOdd
is obtained by embedding the type MyInt
.
Y
has no methods, for its embedded the type Age
has not methods.
Z
has only one method, IsOdd
, which is obtained by embedding the type MyInt
. It doesn't obtain the method Double
from the type X
, for it doesn't embed the type X
.
type MyInt int
func (mi MyInt) IsOdd() bool {
return mi%2 == 1
}
type Age MyInt
type X struct {
MyInt
}
func (x X) Double() MyInt {
return x.MyInt + x.MyInt
}
type Y struct {
Age
}
type Z X
v.m
is a legal promoted method value expression, compilers will normalize it as the result of changing implicit address taking and pointer dereference operations into explicit ones in the corresponding full form selector of v.m
.
v.m
, at run time, when the method value v.m
is evaluated, the receiver argument v
is evaluated and a copy of the evaluation result is saved and used in later calls to the method value.
s.M1
is s.T.X.M1
. After changing the implicit address taking and pointer dereference operations in it, it becomes (*s.T).X.M1
. At run time, the receiver argument (*s.T).X
is evaluated and a copy of the evaluation result is saved and used in later calls to the promoted method value. The evaluation result is 1
, that is why the call f()
always prints 1
.
s.M2
is s.T.X.M2
. After changing the implicit address taking and pointer dereference operations in it, it becomes (&(*s.T).X).M2
. At run time, the receiver argument &(*s.T).X
is evaluated and a copy of the evaluation result is saved and used in later calls to the promoted method value. The evaluation result is the address of the field s.X
(a.k.a. (*s.T).X
). Any change of the value s.X
will be reflected through the dereference of the address, but the changes of the value s.T
have no effects on the evaluation result, that is why the two g()
calls both print 2
.
package main
import "fmt"
type X int
func (x X) M1() {
fmt.Println(x)
}
func (x *X) M2() {
fmt.Println(*x)
}
type T struct { X }
type S struct { *T }
func main() {
var t = &T{X: 1}
var s = S{T: t}
var f = s.M1 // <=> (*s.T).X.M1
var g = s.M2 // <=> (&(*s.T).X).M2
s.X = 2
f() // 1
g() // 2
s.T = &T{X: 3}
f() // 1
g() // 2
}
package main
type I interface {
m()
}
type T struct {
I
}
func main() {
var t T
var i = &t
t.I = i
i.m() // will call t.m(), then call i.m() again, ...
}
The Go 101 project is hosted on Github. Welcome to improve Go 101 articles by submitting corrections for all kinds of mistakes, such as typos, grammar errors, wording inaccuracies, description flaws, code bugs and broken links.
If you would like to learn some Go details and facts every serveral days, please follow Go 101's official Twitter account @zigo_101.
reflect
standard package.sync
standard package.sync/atomic
standard package.