Launch your project today with this Free $200 USD credit (Valid for 60 days) on DO

Digital Ocean is the platform that I use to host most of my projects, including this blog, and I can sincerely recommend it to my readers, using this link will not generate an extra cost on you, it helps me to keep the blog costs low and you will also get $200 USD in credit to materialize your ideas.

Table of contents

Go: Structs, inheritance, polymorphism and encapsulation

Go: Structs, inheritance, polymorphism and encapsulation

As I already mentioned to you in the introduction to the Golang or Go programming language , this language does not have a reserved word for dealing with classes, but uses structs to emulate features such as inheritance, polymorphism, encapsulation and other properties of classes that you have probably used in other programming languages.

Hey! did you know that I wrote a completely Free Go programming language tutorial?, you can find it directly in the top menu bar or clicking this box.

Structs in go

A struct in go is a collection of fields. They are defined with the keyword type followed by the name to assign and the word struct.

type Videogame struct {
    Title string
    Year int
}

Creating instances of a struct in go

Once the structure of our struct is defined, we can create instances of it.

var myVideogame = Videogame{Title: "Nier", Year: 2017}
// The code below is valid only inside a function
myVideogame := Videogame{Title: "Nier", Year: 2017}

It is also possible to create an instance by declaring the data type and then assigning a value to the fields.

var myVideogame Videogame
myVideogame.Title = "Nier"

If we do not specify a value, the respective zero value will be assigned to the go variable type .

fmt.Println(myVideogame)
// {Nier 0}

Anonymous fields in structs

In go, it is possible not to declare the name of the field of our struct and to place only the data type. Done this way, the fields will adopt the name of the data type and we can access them using them.

type Videogame struct { 
    string 
    int 
}

myVideogame := Videogame{string: "Title", int: 2017}
fmt.Println(myVideogame)
// Print {Title 2017}

What if we want a struct with many fields of the same type? Well, obviously this feature will not work if we have multiple fields of the same type in our struct.

Privacy and encapsulation of structs

To mark a struct, function or variable as private or public, as well as their respective fields for the struct, it is sufficient to declare the first letter of the field with upper or lower case, for public and private, respectively.

Personally, I think this is one of the most convuluted aspects of this language, why? because when you need to look for a private field, you would probably need to use regex, instead of just searching by the word private.

type MyData struct {
    Name string // public
    account string // private
}

Having said that, accessing a private entity from another module, unrelated to where it is declared, will be impossible. Whereas public entities are accessible from any module, even if the struct is not declared there.

  • capitalized, public, accessible inside and outside the package where it is declared.
  • lowercase, private, only accessible inside the package where it is declared.
// Videogame struct representing a videogame entity
type Videogame struct {
    Title string
    Year int
}

Composition and structs in go

We can group functionalities that involve our structs by declaring functions that access them.

To access struct instances in functions we need to place a pair of parenthesis between the keyword func and the function name. These parentheses contain the name we will use to access the struct instance, followed by the destructuring character and finally the struct name.

func (myStructVariable *Videogame) PrintYear(){
    fmt.Println(myStructVariable.Year)
}

Inside the function, the struct fields will be available using pointers, we pass it inside the parenthesis with the destructuring character.

func (myStructVariable *Videogame) IncreaseYear(){
    myStructVariable.Year = myStructVariable.Year + 1
}

And once this “method” is declared, we can call the method we created through an instance of the struct. Notice how it does not receive any argument but behaves as a method.

videogame.IncreaseYear()

Customize the printout of a struct in go

If we declare a function to customize the console output called String, we will replace what the struct returns when we print it.

Note the absence of the destructuring operator in the parenthesis in the first set of parentheses and how I use the Sprintf method of the go fmt-package to return a string.

func (myStructVariable Videogame) String() string { 
    return fmt.Sprintf("Title: %s, Year: %d", myStructVariable.Title, myStructVariable.Year) 
}

Now every time we print a struct we will get a text string with the syntax we declared.

fmt.Println(myVideogame)
// Title: Nier, Year: 2017

Constructors in go

Go does not have a mechanism similar to the constructors of other object-oriented languages. However, it is quite common to create a function that emulates them, returning an object already initialized with the parameters we want to apply.

func NewVideogame(year int, title string) *Videogame {
    return &Videogame{
        Title: title,
        Year: year,     
    }
}

This copy of a constructor is able to create instances of our struct Videogame.

nier := NewVideogame(2017, "Nier")
fmt.Println(*nier)
// {Nier 2017}

Inheritance in go structs

Go does not have a word for declaring inheritance in structs, however it does have something similar. For a struct in go to have all the fields declared by another struct, we pass the latter as an anonymous field.

type Person struct {
    Name string
    Lastname string
}

type Professor struct {
    Person
}

Polymorphism using interfaces in go

You can think of an interface as a kind of “blueprint” that tells an object what functions it must implement to work, each object can implement those functions as it wants, but it must implement them.

For example: Think of an abstraction that represents each animal that breathes, if an animal breathes, we take for granted that it must must implement the “breathe” interface, but each animal will implement it as it likes, some will breathe using noses, others gills, some will do it very slow, others very fast, but all must implement a method to breathe.

Based on the above, in Go interfaces are a method to differentiate the behavior of different objects. An interface will be in charge of calling the method that we specify corresponding to its struct type.

See how the interface declares that the type fourSidesFigure has a method called area that returns a result of type float64.

type fourSidesFigure interface{
    area() float64
}

Next we declare a different method called area for both struct square and struct rectangle, just as we did in the “Composition and structs” section of this entry.

type rectangle struct {
    base float64
    height float64
}

type square struct {
    base float64
    height float64
}

func (c square) area() float64 {
    return c.base * c.base 
}

func (r rectangle) area() float64 {
    return r.base * r.base 
}

Now we will create a function that will receive as argument any type fourSidesFigure, and will execute its respective area. method.

what’s an interface for in Go?

Here it is necessary to put special emphasis, observe how the argument that we pass to the function is the interface that we created, we are not interested in what type of struct we are passing as argument, the only thing that we care about is that this argument satisfies the interface.

In this case the interface is fourSidesFigure, hence, whatever we pass it as an argument must have a method called area that returns a float64.

When we use interfaces we can guarantee that the code works at a higher level, and we can leave the details of the low level functioning to its respective object, we can replace the implementation of the interface as long as it satisfies the interface’s signature.

type fourSidesFigure interface{
    area() float64
}

func calculate (f fourSidesFigure) {
    fmt.Println("Area", f.area())
}

To call the respective method we just call the function by passing it an instance of the struct as an argument.

mySquare := square{base: 2}
calculate(square)
mirectangle := rectangle{base:2, height: 4}
calculate(mirectangle)
// Area 4
// Area 8

Surely if you come from other languages you will find Go syntax quite capricious, but I am sure you will be mastering it in a very short time, if I did it for you it will be a piece of cake.

Eduardo Zepeda
Web developer and GNU/Linux enthusiast. I believe in choosing the right tool for the job and that simplicity is the ultimate sophistication. Better done than perfect. I also believe in the goodness of cryptocurrencies outside of monetary speculation.
Read more