Defining your own types
Of course Go allows you to define new types, it does this with thetype
keyword: type foo int
This creates a new type foo
which acts like an int
. Creating more sophisticatedtypes is done with the struct
keyword. An example wouldbe when we want record somebody’s name (string
) and age (int
) in a singlestructure and make it a new type:
package main
import "fmt"
type NameAge struct {
name string // Both non exported fields.
age int
}
func main() {
a := new(NameAge)
a.name = "Pete"
a.age = 42
fmt.Printf("%v\n", a)
}
Apropos, the output of fmt.Printf("%v\n", a)
is &{Pete 42}
That is nice! Go knows how to print your structure. If you only want to printone, or a few, fields of the structure you’ll need to use .<field name>
. Forexample to only print the name:
fmt.Printf("%s", a.name)
More on structure fields
As said each item in a structure is called a field. A struct with nofields: struct {}
. Or one with four fields:
struct {
x, y int
A *[]int
F func()
}
If you omit the name for a field, you create an anonymous field (((field,anonymous))), for instance:
struct {
T1 // Field name is T1.
*T2 // Field name is T2.
P.T3 // Field name is T3.
x, y int // Field names are x and y.
}
Note that field names that start with a capital letter are exported, i.e. can beset or read from other packages. Field names that start with a lowercase areprivate to the current package. The same goes for functions defined in packages,see for the details.
Methods
If you create functions that work on your newly defined type, you can take tworoutes:
- Create a function that takes the type as an argument.
func doSomething(n1 *NameAge, n2 int) { /* */ }
func (n1 *NameAge) doSomething(n2 int) { /* */ }
This is a method call, which can be used as:
var n *NameAge
n.doSomething(2)
Whether to use a function or method is entirely up to the programmer, but if youwant to satisfy an interface (see the next chapter) you must use methods. If nosuch requirement exists it is a matter of taste whether to use functions ormethods.
But keep the following in mind, this is quoted from [go_spec]:
If
x
isaddressable and&x
’s method set containsm
,x.m()
is shorthand for(&x).m()
.
In the above case this means that the following is not an error:
var n NameAge // Not a pointer
n.doSomething(2)
Here Go will search the method list for n
of type NameAge
, come up empty andwill then also search the method list for the type *NameAge
and willtranslate this call to (&n).doSomething(2)
.
There is a subtle but major difference between the following type declarations.Also see the Section “Type Declarations” [go_spec]. Suppose we have:
// A Mutex is a data type with two methods, Lock and Unlock.
type Mutex struct { /* Mutex fields */ }
func (m *Mutex) Lock() { /* Lock impl. */ }
func (m *Mutex) Unlock() { /* Unlock impl. */ }
We now create two types in two different manners:
type NewMutex Mutex
.type PrintableMutex struct{Mutex}
.
NewMutex
is equal to Mutex
, but it does not have any of the methods ofMutex
. In other words its method set is empty. But PrintableMutex
has__inherited the method set from Mutex
. The Go term for this is embedding. In the words of [go_spec]:
The method set of
*PrintableMutex
contains the methodsLock
andUnlock
bound to its anonymous fieldMutex
.