Pages

Tuesday, February 13, 2018

Golang for dummies - Structs and Methods

This is Part One of a two-part session on structs and interfaces. 


Before we can talk about the scary thing that is interfaces in Go, we have to talk about structs. 

Structs are pretty intuitive. They are a way to organize your code into cohesive ideas and then make it convenient to access individual properties of the struct, pass them, around, and generally make your life better. I did not experience structs as particularly difficult to understand. Thanks, Go!

Structs are a composite type. This is as opposed to primitive data types such as int, bool, etc - of which Go has...a surprisingly large amount. Find them listed here.

Structs are basically Golang's version of what in, for instance, Java, would be classes This is a bit oversimplified, and really just meant to get you an idea to get you started.

Structs have fields. They can be accessed via the dot operator (.)


We declare a struct according to the following format:

type <StructName> struct {<fields>}




On lines 9-12, we declare the Unicorn struct. It has three fields: age, name, and color. If two fields have the same type, we can put them on the same line, as is shown with name and color on line 11. Fields do not need to be primitive types, by the way; they can be other structs as well.

On line 14, we declare cornyan instance of type Unicorn. We can see from the output of line 17 that the fields have default values of 0 and the emptry string. We later assign corny's fields values, and those are shown in the last line of the output.

We say, "A Unicorn has a name". This has-a relationship is the definition of what should be a struct field.


We can also create a new Unicorn with its fields at initialization. This is called a literal.



If you use a literal to instantiate a struct, all the fields must match types and must be in the order that they appear in the struct declaration. The following things do not work:





Only if you specify the field name in the declaration will it work regardless of order:



Structs can also be nested. 
 
 
Notice how we didn't have to call pegasus.MythicalBeast.Name. In fact, pegasus.Name was sufficient. Because a Unicorn is a MythicalBeast, it has an Age and a Name. Just like any other MythicalBeast.


We cannot talk about structs without talking about Methods.

Methods are functions that get called on a specific type, called a receiver. This sounds a lot like Java or Ruby classes. The difference is that we do not declare them as part of a struct declaration. Instead, we tell the method which type can "receive" the method. The receiver can be any data type (which is nice when you need to add functions to a type that’s not yours).
Methods are called using the dot(.) operator, just like struct fields.

The basic format for a method declaration is as follows:

func (<ReceiverType>) <MethodName>(<args>) <returnType> {}


You can see that on line 13 we have a heal() Method that takes a *Witch as a receiver. Then, on line 20, we can call heal()on bibi, a *Witch, with the dot operator.
By the way, bibi is a *Witch, not a Witch, because we created her with the new() function, which returns a pointer to an object. It turns out that methods, unlike functions, do not care if you pass a pointer or a value:


In general, you should have your methods accept pointers so that:
a. the value does not get copied on each method call and

b. we can potentially modify the receiver in the method.

For more on pointers, please read my previous post on pointers.

Bibi Blocksberg was a staple of my childhood. Learn more about her.




Embedded Structs

Remember nested structs, above? When structs are nested, we have access to the fields of the parent struct. With methods, the same holds true.



bibi, a Witch, is also an Adventurer.
bibi has additional skills that the average joe does not have.
They both have the ability to walk(), but only bibi can heal(). bibi can use Adventurer methods because the Witch struct embeds the Adventurer struct, ("a Witch is an Adventurer) and as such has access to all the methods that Adventurer implements. The reverse does not hold true, because joe is not a Witch. He cannot use Witch methods. Methods are pretty awesome. Since they are not defined inside a struct, but rather with a receiver, you can add methods to existing types with very little trouble. I personally found the receiver concept a little tricky at first (why are there now three things in my function declaration?) but it helps me to think of the receiver as saying "This method operates on this struct". I hope this was helpful to some of you!

Please stay tuned for the next installation of my golang journey, regarding Interfaces.
Sources: 
    Golang playground
    Tour of Go
    Golang book chapter on structs




No comments:

Post a Comment