Home Type, Struct, Type Embedding and Constants
Post
Cancel

Type, Struct, Type Embedding and Constants

type is not a type alias, which is different than Scala for an example. The new type is not compatible, assignable or comparable to the original type. In my mind this is better than type aliasing because type aliasing, if overused, is in many cases redundant and confusing. type timeInSeconds int and type timeInMilliSeconds int are not compatible. You need to cast from one to the other (or just for correctness to int and then to the other) to convert.

1
2
3
4
5
type timeInSeconds int
type timeInMilliSeconds int

var seconds timeInSeconds = 10
var millis timeInMilliSeconds = timeInMilliSeconds(1000 * int(seconds))

Struct

Declaring a new type person

1
2
3
4
type person struct {
	name string
	age  int
}

Instantiating an instance of person

1
2
3
4
a := person{
	name: "Some name",
	age:  10,
}

We can instantiate person without naming the fields, given that we provide all fields in the correct order. If we don’t provide any of the fields, i.e. use {} we end up with an object with all its fields set to its corresponding zero value.

1
2
a1 := person {"name 1", 1}
a2 := person {}

There is also the built in function new which creates a pointer to a zero allocation of the type, thus the following two are identical

1
2
a2 := &person {}
a3 := new(person)

Finally there is a notion of anonymous struct

1
2
3
4
5
b := struct {
	description string
}{
	description: "This is an anonymous struct!",
}

Type Embedding

Let’s look at the following example using the person type we have defined earlier, we define a new type player.

1
2
3
4
5
type player struct {
	person
	favouriteGame string
	int
}

We notice that we have two anonymous fields, one of type person and the other of type int. The question now, how do you access those? The field names are implicit and they are the same as the type.

1
2
3
4
5
p := player{person{"jack", 23}, "game", 1}

fmt.Println(p)              //  {{jack 23} game 1}
fmt.Println(p.person.name)  // jack
fmt.Println(p.int)          // 1

Go also give us another interesting feature, you can access the fields of the embedded type, person in our case, directly using p.name for example… just like that.

1
fmt.Println(p.name)         // jack

If both the embedded and the embedding type has fields holding the same name, then you will need to use p.name for the embedding type and p.person.name for the embedded type. Let’s redefine our type player and create an instance of it.

1
2
3
4
5
6
7
8
type player struct {
	person
	favouriteGame string
	int
	name string
}

p := player{person{"jack", 23}, "game", 1, "Mo"}

As you can see we have defined name twice. In that case the one defined in the player struct will hide the one from person struct.

1
2
3
4
fmt.Println(p)              // {{jack 23} game 1 Mo}
fmt.Println(p.person.name)  // jack
fmt.Println(p.name)         // Mo
fmt.Println(p.int)          // 1

Notes:

  • we can perform conversion from one struct to the other if they have the same exact fields.
  • Anonymous struct doesn’t require explicit conversion, if types are identical.
  • Field order is part of a type. Different field orders means different types, even if the fields are identical.
  • If we are trying to access a field in a pointer to a struct, we don’t need to explicitly use * to get to the contents of that structs, we can use . directly. That is the following two lines of codes will print the exact same thing
1
2
fmt.Println(b.description)
fmt.Println((&b).description)

Pointers

  • Everything in Go is pass by value.
  • Pointers is for pass by reference.
  • We use & similar to C to get the address of a var.
  • We use * similar to C again to get the value that a pointer is pointing to.

Constants

  • Constants have a parallel type system, they could have a type or a kind!
  • iota is an interesting concept, the simplest use for it is to create an enum like constants with incremental value.
  • iota can be used for different increments and even with the shift operator iota
1
2
3
4
5
const (
	c0 = iota  // c0 == 0
	c1 = iota  // c1 == 1
	c2 = iota  // c2 == 2
)

Which can also be written as

1
2
3
4
5
const (
	c0 = iota  // c0 == 0
	c1         // c1 == 1
	c2         // c2 == 2
)

Resources

  1. Ultimate Go Programming
  2. The Go Programming Language
  3. Go Documentation
  4. A tour of Go
  5. Go wiki: Switch
  6. The Go Programming Language Specification
  7. Go by Example
This post is licensed under CC BY 4.0 by the author.
Contents

-

Commandline Arguments and Flags

Comments powered by Disqus.