Types and Structures

New Types

You can create your own types in Go using the type keyword. For example:

type Score int

func show(s Score) {
        fmt.Println("mark =", s)
}

func main() {
        s := Score(5)  // s is declared to be of type Score
        show(s)
}

In this example, int is said to be the underlying type of Score.

One reason to create types is to make your source code clearer. Another reason is that you can define methods on a type Score, but you cannot define methods on a basic type such as int.

This sort of assignment is permitted:

show(5)

Strictly speaking, 5 is an int, and so it is not of type Score. However, because Score has an underlying type of int, this statement is not an error.

Here is an example where you do get a type error. Suppose we add a new type called Mark:

type Mark int

And then call this code:

m := Mark(3)
show(m)  // compiler error: m is wrong type

This doesn’t compile because m is of type Mark, but show expects a parameter of type Score. Even though both Mark and Score have the same underlying type, one cannot be assigned to the other.

See the Go specification for the exact type equivalence rules.

Structures

Go structs are similar to the structures in C-like languages. For example, here is how you can represent a 2-dimensional point:

type Point struct {
        x, y int
}

One way to create a struct is to use the new function like this:

p := new(Point)  // new initializes p.x and p.y to their zero values
fmt.Println("p.x: ", p.x)
fmt.Println("p.y: ", p.y)

The new function always sets the variables in a struct to their zero values. If you want to initialize them to some other values, you can use a composite literal like this:

dest := Point{-8, 11}   // Point{-8, 11} is a composite literal
fmt.Println("dest.x: ", dest.x)
fmt.Println("dest.y: ", dest.y)

Note that new is not used here.

Here is an example of a struct built from two points:

type Segment struct {
        start, end Point
}

You can create a new Segment using a composite literal like this:

trip := Segment{Point{1, 2}, Point{3, 4}}
fmt.Printf("%v\n", trip)   // %v prints Go values in a nicely formatted way

This prints:

{{1 2} {3 4}}

Another way to write a composite literal is with explicit parameter names:

trip := Segment{start: Point{1, 2}, end: Point{3, 4}}

When explicit names are given like this, the order of the parameters doesn’t matter. The above declaration is the same as this:

trip := Segment{end: Point{3, 4}, start: Point{1, 2}}  // order doesn't matter
                                                       // when you provide
                                                       // names

Named composite literals are especially useful for longer, more complex structs where the order of parameters can be hard to remember.