Illustatration by Sam Croswell https://dribbble.com/shots/1749785-Go-Lang-Gopher

Comprehensive Go

pancy
Code Zen
Published in
12 min readOct 1, 2014

--

This post talks about Go’s more advanced functions, pointers, methods, structs and interfaces.

Functions

Functions in Go are pretty much straightforward, with special spices for your productivity of course. Til now we have seen only one function:

func main() {}

Just like C++, Go compiler needs to start at the main function. It’s like Timesquare for tourists. Let’s get into a general function signature when you declare any function in Go:

func funcName(arg1 type, arg2 type, ..., argN type) returnType {}

Just like in variables, you indicate your function’s return type on the far right of the signature. In fact, adding an optional return variable name, you can do an implicit return like this:

https://gist.github.com/jochasinga/3fc186da6844c7d5ada6

This way, Go set up that variable to be ready before getting to the business executing what’s inside your function block, and you no longer have to tell go what to return. (Don’t forget a pair of parentheses.)

Remember mass assignment in other languages? When you have several variables with a same type, you are better off lining ‘em up with commas with a single type declaration.

func colormap(r, g, b uint8) (color map[string]uint8) { ... }

Go functions can also return multiple values. Just line ‘em up with commas in between, and you’ll be fine. (Don’t forget a pair of parentheses.)

https://gist.github.com/jochasinga/40fc967e949960fedfb3

Also remember that multiple values are returned in order almost like an array. If you need to assign them to another variables make sure they are lined up in exact order.

c, d := a, b

Go has built-in three statements to handle exceptions called defer, panic and recover. defer makes sure a function always gets called at the end of a process, regardless of any exceptions (or panics) and before any return statements. You can think of it as similar to finally in try-except-else-finally suite in Python, which is also a way to make sure something is executed not matter what. defer can be useful in processes like file I/O or buffer operations when you need to assure the file is closed. For instance:

https://gist.github.com/jochasinga/d7ddfe4eaf0c00aea117

The above code calls file.Close() just before the program exits. You can get away with calling the expression yourself on the last line, but using defer is a better choice. With defer, you can write a function to free up your resource next to the line that opens it up, making the code easier to read. Also, the deferred functions will always be called before any return in your program, even when run-time panics occur.

You can defer an anonymous function, like this:

defer func() string { fmt.Println("Bye bye") } ()

Just remember that deferring a function is calling it, and thus you will need a pair of parentheses after your function.

The panic statement calls a run-time error “shout out”, taking a string for it’s panicky holler. We usually handle panics with recover statement. (Think try-catch). It stops the panic and return the “holler” string passed in as an argument to the panic function. We can use defer with recover to assure that recover gets called when a panic occurs (because a panic stops and exits program execution, which triggers defer).

https://gist.github.com/jochasinga/978aaf52d853a41f37d8

A panic usually means some human errors such as an array out-of-bound error, uninitialized map, and other programmer-made exceptions.

Pointers

Here is one of the pet-peeves of web-scripting developers—pointer. Let’s start with a cartoon about another kind of pointer (which is some what irrelevant but serves to prevent you from closing the browser and running away to JavaScript or Ruby).

Funny cartoons from http://xkcd.com/734/

This cartoon, hilarious as it was, does bear some concept of pointers in Go. The laser dot, once created by the laser source, didn’t seem to stick to it, leaving an opportunity for the kitty who knows about pointer more than the stickman to gain access to its quantum energy, consume it and beam the stickman away to limbo!

Let’s get to a quick low-level memory primer since we are probably scripting junkies who don’t really micro-manage that much. Let’s me make myself clear to you that my relationship with C++ is somewhat “complicated,” and I’m also walking down this memory lane with you.

You see, when you create a variable and assign it a value, you’re probably coming up with an “alias” or a concept of that value.

var a uint8 = 5

The code tells the computer to allocate or reserve a byte (actually, half a byte in 32-bit a byte in 64-bit architecture) of your precious memories to store the value 5, or more correctly, 00000101 in computer’s tongue. a does not refer to the memory slot that is actually storing the value. It’s just there, somewhere over the rainbow.

Now, let’s say we change our mind and assign another value to a.

a = 10

We actually just swapping a from carrying a concept of 5 to carrying another of 10. Since you’ve told a to hold only a byte, it will just throw away the last value to carry the new one. At this point, you end up having already bits of memories used up behind you. This is why Garbage Collector comes in handy, cleaning up after our mess, or we will soon run out of memories at some point.

Go has the concept of pointer to let you decide memory allocations and access patterns, but you can’t do arithmetics on pointers. Pointers, altogether with static- and strong-typed, means speed, since you’re helping computer to manage some stuff and ease its burdens.

To actually refer to the address of our stored value in the memory slot, address-of operator `&` is prepended to the variable, and that variable will return a pointer to the memory, which is usually in a hexadecimal number like 0x6b0820.

fmt.Printf("The address of a is: %p\n", &a) 

The above line print out the address of a, not the value that a is carrying (`%p` is a string interpolation for pointer).

To create assign this address to a variable, you need to create a pointer variable by prepending a type modifier `*` to the variable’s type name.

var aPtr *uint8 = &a

Sometime, it can be confusing when to have that star before a variable, but it’s as simple as this:

aPtr = &a        // This points to the hex address i.e. 0x6b0820
*aPtr == 10 // This is true, since a = 10
a == *(&a) // This is true, *(&a) gives you 10

The process of acquiring the value from a pointer is call dereferencing or flattening.

Let’s write a simple function that tries to change the value of an argument.

func changeAsInObama(x, y int) { 
x = y
}
func main() {
x := 5
y := 10
changeAsInObama(x, y) // change x to 10
fmt.Println(x) // x is still 5!
}

in Go, arguments are copied to the function. How can we actually change the value at that exact location in memory? Without much thought, the first thing to do is finding out where that value resides. It’s like visiting a friend to retrieve an overdue book he borrowed and not just asking him on the phone. You need his address.

Let’s revisit our changeAsInObama function, but this time, we introduce a pointer variable name xPtr.

func changeAsInObama(xPtr *int, y int) {
*xPtr = y // make the pointed value change
}
func main() {
x := 5
y := 10
changeAsInObama(&x, y) // pass in the address of value
fmt.Println(x) // x is now 0

Like our friend’s analogy, instead of just referencing to where your friend lives by having his address or phone number on your phone, you actually walk up to his address for your book. To get to where the pointer is pointing (figuring out where your friend’s home is), you prepend an ampersand `&` before the variable to return the location. You are now ready to break into his house and abuse him as you want!

Go has a built-in new method that you can use to initialize a pointer with.

x := new(int) // allocate memory to fit an int and return a pointer
var x *int // same as the above

Pointers are rarely used with Go’s basic types, but they’re useful with structs (or Go’s “sorta” classes), which we’ll see next.

Structs & Methods

Structs are the only object-oriented option in Go has no concept of class. This is a bold move by Go’s creators. Although you can hardly get into real programming tasks without using a few classes, many modern web languages have different approaches to classes. JavaScript use prototypes; Ruby and Python have mixins in addition to class-base inheritance. To me, classes are not everything, except if you program in Java. See a discussion on composition over inheritance.

A struct is a type that contains named fields. Like a class, it is a description of a concept of a being. Here is a struct describing a gopher:

type Gopher struct {
// note there are no commas between fields unlike maps
kingdom string
color map[string]int
num_legs int
height float32
}

You will notice the similarity to class, except that a struct has no constructor method. To create a new instance of Gopher, we write:

var chip Gopher

Gopher is now a type. This initialize a Gopher named chip with zeroed fields, meaning all fields are set default to 0, which means “” for type string, map[] for map, 0 for int and 0.0 for float. We can also use new keyword:

chip := new(Gopher)

This allocates memory for all fields in chip the Gopher, then sets them to 0 and returns a pointer *Gopher instead, right to the very den where chip calls home. (Remember that *Gopher holds a hex of address where chip is)

We can also initialize and assign values to chip right away.

chip := Gopher{
kingdom : "Animalia",
// chip has ultramarine hair color
color : map[string]int{
"r" : 102,
"g" : 255,
"b" : 255,
},
num_legs : 4,
// height in feet
height : 1.5,
}

If you remember the order of fields, you can leave out the names:

chip := Gopher{
"Animalia",
map[string]int{
"r" : 102,
"g" : 255,
"b" : 255,
},
4,
1.5,
}

This reads “chip is a Gopher and has a kingdom Animalia, a color of ultramarine, a number of legs of 4 and a height of 1.5 feet.”

We can access the g struct’s values using `.` dot notation.

https://gist.github.com/jochasinga/1ffad9d7be8c374fff19

Now, we can write a function that takes our Gopher as an argument. Since arguments are always copied to a function in Go, we might want to take a pointer to our struct instead.

https://gist.github.com/jochasinga/11a747a45a943982a7f4

Now, you’re thinking, what’s the use of an instance without methods? The above report function could have been a method of our Gopher instance g. In Go, methods are declared outside of a struct with a signature like this:

func (g *Gopher) Report() { 
// method's body
}

This function belongs to our Gopher now. We can replace our function call report(&chip) with a method call on our instance chip.report()

Methods can be called on any types other than structs. However, they can’t be called on Go’s existing types i.e. int, float64, string. To declare a method for these basic types, we need to alias or “name” the type like this:

type myStr string  // Name the type string as a new typefunc (s myStr) Print() {
fmt.Println(s)
}
var str myStr = "Hello"
func main() {
var str1 myStr = "Hello"
str1.Print() // print "Hello"
str2 := "Hola"
str2.Print() // Error: str.Print undefined ...
}

Embedded Type (Go’s Inheritance)

Our last Gopher struct exhibited a has-a relationship. i.e. Gopher has-a height of 1.5 feet. However, with inheritance, you will want to say that struct B is-a struct A, and has fields of A.

A struct can inherit another struct by embedding the latter as a type inside the first, without naming it. Thus the name Embedded Type.

type Creature struct {
origin string
kind string
age uint64
}
// Sphinx is a Creature with a unique carnivore field of its own
type Sphinx struct {
Creature
carnivore bool
}

Now, the Sphinx is-a Creature, and it contains all the fields and methods associated to a Creature, plus it does (or does not) eat meat!

Interfaces

Are you much of a traveler? Most of us have gone to at least another part of the world that spoke a different language from ours. Let’s say you were in Japan. You were walking out of your ryokan and you bumped into a josei. You wanted to be friendly by greeting hergood morningand asking if she lives around here. You greeted “Ohayo” and asked “Anata wa chikaku sunde imasu ka?”

How about if you were in some other countries, say, Thailand? You will need a different communication skill to befriend a local. That goes the same for other non-english speaking countries. You would probably need to ask Doraemon the cat robot for his magic translation jelly.

Doraemon image from http://hellocoloring.com/doraemon-1-coloring-comicsy-comics-1630.html

Interface is like the translation jellies, or more like Doraemon. Often, Doraemon will make you promise him things you wouldn’t do with his magic gadgets before giving them to you. You and him are making a deal or contract in order for you to gain a skill, in this case, an ability to talk to anyone from any culture.

As long as any instance “satisfies” the contract prerequisite, in this case, implements the method(s) registered in an interface, it can implement the interface as well.

Let’s start with a code that simulates the situation without an interface. We should create a Man struct to represent ourselves, containing a few properties or fields describing any man. In this case, language is what we focus.

https://gist.github.com/jochasinga/7074b122780558e154d1

The Greet method is self-contained, and this is good enough if you’re enjoying the jelly alone. However, if your good buds, James and John, are spending holidays in Thailand and UK, respectively. How would you like to share your new skill so they can greet locals in their areas too?

We abstract our Greet method into an interface Greeter. It is a convention to append your method with -er or appropriate noun suffix, though not required. Now, the picture of the Greeter Jelly becomes clearer.

https://gist.github.com/jochasinga/9f9eee05b648aea04564

This looks like more tedious code with the same result at first. However, what happen if you have a girl friend name Jennifer who is traveling in Philippines and would also like you to share the translation jelly, but she is somewhat a shy person and prefer an option to write her greeting in a letter instead?

https://gist.github.com/jochasinga/7f1f195a5330e77d8c94

An interface is a center of all implemented methods for all your instances in your program to register to. Those who “register” to an interface will be returned with the a promised skill set, and no matter how many variations of this skill set various identities have, as long as they are under the registered one, they can use it.

Conclusion

I hope by now you’re as excited about digging more gopher holes like I am. Go language, despite being statically-typed, is surprisingly simple and consists of minimal features to get familiar with fast.

We all know that any one language isn’t always optimized to do everything, and Go is no exception. Very often, languages were built quickly with a set of goals in the creators’ mind at the time, and once they’ve gone through years of maturity, modern features to solve new problems which were not thought of before were “layered” atop of the previous version rather than “cooked” into. Matured languages like JavaScript is infamous for its unnecessary features that often introduce confusions when the code gets large (it was created in 1o days as a “user-friendly” counterpart of Java at the time). These are an example of the features creators can’t just throw away after all the years because that can mean writing from scratch and also cost the loyalty of its developers.

Therefore, I think it is always worth trying out new younger tools since they were born recently to serve the fresh needs and context of today. (i.e. Node.js was created out of the rise of Google’s V8 engine) And Go, by far, has been giving me the pleasure and addiction of coding in Python, Ruby and Javascript combined; All with the power of a compiled, statically-typed mother C.

Please excuse any mistakes, and collaborative comments and corrections are always welcome. If you like this, please help recommend below, or do write to me at jo.chasinga@gmail.com or @jochasinga.

--

--

pancy
Code Zen

I’m interested in Web3 and machine learning, and helping ambitious people. I like programming in Ocaml and Rust. I angel invest sometimes.