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:

  1. package main
  2. import "fmt"
  3. type NameAge struct {
  4. name string // Both non exported fields.
  5. age int
  6. }
  7. func main() {
  8. a := new(NameAge)
  9. a.name = "Pete"
  10. a.age = 42
  11. fmt.Printf("%v\n", a)
  12. }

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:

  1. 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:

  1. struct {
  2. x, y int
  3. A *[]int
  4. F func()
  5. }

If you omit the name for a field, you create an anonymous field (((field,anonymous))), for instance:

  1. struct {
  2. T1 // Field name is T1.
  3. *T2 // Field name is T2.
  4. P.T3 // Field name is T3.
  5. x, y int // Field names are x and y.
  6. }

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.
  1. func doSomething(n1 *NameAge, n2 int) { /* */ }
  • Create a function that works on the type (see receiver in ):
  1. func (n1 *NameAge) doSomething(n2 int) { /* */ }

This is a method call, which can be used as:

  1. var n *NameAge
  2. 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 contains m,x.m() is shorthand for (&x).m().

In the above case this means that the following is not an error:

  1. var n NameAge // Not a pointer
  2. 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:

  1. // A Mutex is a data type with two methods, Lock and Unlock.
  2. type Mutex struct { /* Mutex fields */ }
  3. func (m *Mutex) Lock() { /* Lock impl. */ }
  4. 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 and Unlock bound to its anonymous field Mutex.