I was reading this slideshow, which says:
var hits struct {
sync.Mutex
n int
}
hits.Lock()
hits.n++
hits.Unlock()
How does that work exactly? Seems like hits
isn't composed of a mutex and integer, but is a mutex and integer?
It's called embedding, hits
is composed of a sync.Mutex
and an int
. This should be true since there is really no inheritance in Go. This is more of a "has a" rather than an "is a" relationship between the members and the struct.
Read here for a more complete explanation
Quoted from the link
The methods of embedded types come along for free
That means you can access them like hits.Lock()
instead of the longer form hits.Mutex.Lock()
because the function Lock()
is not ambiguous.
See the Go-syntax representation of hits
variable:
fmt.Printf("%#v
", &hits)
// &struct { sync.Mutex; n int }{Mutex:sync.Mutex{state:0, sema:0x0}, n:1}
When you declare the variable, it simply initializes the fields in struct with their default values.
Also, compiler automatically sets the name of the embedded struct as a field. So you can also access like:
hits.Mutex.Lock()
hits.Mutex.Unlock()
And you have access to all methods and exported fields (if any) of sync.Mutex
.
It is composition. Using an anonymous field (embedded field), the containing struct will have a value of the embedded type, and you can refer to it: the unqualified type name acts as the field name.
So you could just as easily write:
hits.Mutex.Lock()
hits.n++
hits.Mutex.Unlock()
When you embed a type, fields and methods of the embedded type get promoted, so you can refer to them without specifying the field name (which is the embedded type name), but this is just syntactic sugar. Quoting from Spec: Selectors:
A selector
f
may denote a field or methodf
of a typeT
, or it may refer to a field or methodf
of a nested embedded field ofT
.
Beyond the field / method promotion, the method set of the embedder type will also contain the method set of the embedded type. Quoting from Spec: Struct types:
Given a struct type
S
and a defined typeT
, promoted methods are included in the method set of the struct as follows:
If
S
contains an embedded fieldT
, the method sets ofS
and*S
both include promoted methods with receiverT
. The method set of*S
also includes promoted methods with receiver*T
.If S contains an embedded field *T, the method sets of S and *S both include promoted methods with receiver T or *T.
This is not inheritance in the OOP sense, but something similar. This comes handy when you want to implement an interface: if you embed a type that already implements the interface, so will your struct type. You can also provide your own implementation of some methods, which gives the feeling of method overriding, but must not be forgetten that selectors that denote methods of the embedded type will get the embedded value as the receiver (and not the embedder value), and selectors that denote your methods defined on the struct type (that may or may not "shadow" a method of the embedded type) will get the embedder struct value as the receiver.