Simple Talk is now part of the Redgate Community hub - find out why

What is the Go Language, and Why is it Useful?

When Google announced the 'Go' language in 2009 we were all underexcited. After all, a compiled, statically typed language in the tradition of Algol and C isn't that radical, especially one that eschewed generic programming, implicit type conversions, assertions, inheritance and pointer arithmetic. However, it has proved to be robust, highly-portable, simple to use, and productive to work with. Ed Elliott reckons that it is definitely worth checking it out.

What is Go?

The Go documentation describes Go succinctly:

“Go is expressive, concise, clean, and efficient. Its concurrency mechanisms make it easy to write programs that get the most out of multicore and networked machines, while its novel type system enables flexible and modular program construction. Go compiles quickly to machine code yet has the convenience of garbage collection and the power of run-time reflection. It’s a fast, statically typed, compiled language that feels like a dynamically typed, interpreted language.”

What this means is that it is:

  1. a relatively simple language, certainly compared to c++ and to some extents c#.
  2. easy to write programs that scale on multicores
  3. networked software
  4. fast to compile, even the largest applications take seconds to compile
  5. statically typed for safety

If you are going to write something that will serve network requests of some sort, and will need to scale to a significant numbers of requests, then I would say that Go should be one of the languages that you ought to consider using. Furthermore, if you are looking for a cross platform language that compiles to a console application which will run on the target machine without any dependencies, you also can’t go too far wrong in looking at using Go.

What is the history?

Go was invented by three engineers at Google, supposedly impatient at the time it took to build the C++ source at Google. The idea was to build a replacement for C++ that would mean that where they had chosen between C++ for performance with both its complexity and slow build times, and something like Java or Python. The inventors decided that a new language was the only way forward, and that language ended up as Go.

What is it best suited to?

Go is best suited to networked services that need to scale. Go has a number of features that mean that it is well-suited to building applications that have to be fast. The first of these ‘turbo’ features is Go’s ability to scale across multiple cores by making multi-threaded programming straightforward. Go uses a model for concurrency called CSP or ‘Communicating Sequential Processes’. The idea is that, instead of sharing variables and having to control access to those variables, shared values are passed between threads using channels. If only one thread can access a value at a given time then you don’t need to lock the variable. The Go authors have reduced this to a slogan:

“Do not communicate by sharing memory; instead, share memory by communicating.”

In a practical sense what this slogan means is that, instead of doing it by using locks, mutexes or ReadWriterLockSlims, we pass the object that contains the variable from one GoRoutine (akin to a thread) to another – you use the object and then, when you are finished, you pass it onto the next channel or throw it away.

Cross Platform

A further benefit of Go is that it is a cross-platform language and has a runtime that works on Windows, FreeBSD, Mac OS X and Linux. When you compile your Go binary, the runtime is compiled into the executable and that can be copied onto a machine and run – there is no need to run an installer or get any dependencies for your application to run. Go also includes support for building on one architecture but targeting another so from a 32-bit Windows XP machine, should you be unfortunate enough to have a Windows XP machine, you can build a 64-bit binary to run on a Linux machine.

Installing Go

To get going with Go you need to install it on your development machine, the latest version can be downloaded from:

https://golang.org/dl/

Once you have downloaded and either installed the MSI on Windows or extracted the .tar.gz on Linx/FreeBSD/Mac, you need to set a couple of environment variables:

The first environment variable is GOROOT which needs to be set to the value of the path to the root installation folder, assuming you installed to “c:\go” on windows or “~/go” then you would use the console command:

"set GOROOT=c:\Go" or "export GOROOT=$HOME/go"

You should also update your path variable so that it includes the "%GOROOT%/bin" or "$GOROOT/bin” folder.

The next step is to set up your workspace folder, which stores downloaded packages etc. and source code. The environment variable “GOPATH” should point to that folder:

set GOPATH=c:\Users\User.Name\Documents\gocode" or "export GOPATH=$HOME/gocode

To test that you have everything set up correctly, you should open a command prompt and type “go env“, and make sure you have a value under GOROOT and GOPATH.

Our first Go Program

If you create a new folder under “%GOPATH%” or “$GOPATH” and call it “src”, then inside that create a folder called “simple-talk” and inside that create a text file called “main.go” and write:

package main

 

import fmt

 

func main(){

        fmt.Println(Hello Simple Talk!)

}

https://play.golang.org/p/Uet9PBWaCo

Save this and from the same directory run “go build” which should run without any errors and complete within a second or so. If you are on Windows you will have a “simple-talk.exe” or on other platforms a “simple-talk” executable. If you run the executable it should give you the output “Hello Simple Talk!”.

If we take this line-by-line, we have:

package main” This is like a namespace in C#, but differs from a namespace in C# in that you can’t put whatever you like as the package name. An application always starts with “package main” and most smaller programs do not use any other packages: Instead, packages are used to load libraries to control visibility outside of the library.

The next line is ‘import "fmt"‘. This is importing the “fmt” package which we are using to print out some text: In Go you need to import the name of any packages that you are going to use. Import is like adding a reference to a dll and using the using keyword in C#. When you import a package, the targeted package is compiled into the Go binary; so if you import a package that you do not use, the compiler will throw an error and force you to “use it or lose it”.

We then have “func main(){” which is the start of the main func – main is the entry point to every Go app. In this line we also see that Go is based on the C style of languages, preferring to use cURLy braces to separate code rather than whitespace or keywords.

The second to last line is:

    `fmt.Println("Hello Simple Talk!")`

This uses the “Println” method which is in the “fmt” package and simply prints out a string. The last line is the cURLy brace that ends the method.

The first of a couple of points here is the lack of semi-colons anywhere, Go does use semi-colons and while you can end a line with one, the standard Go tools will, if configured, remove the semi-colons. When the code is compiled, semi-colons are added for you but are only seen by the compiler. This means that your source code is mostly free of semi-colons in order to aid readability.

The second point is that this is exactly how this Go program should look: The writers of Go are very opinionated and say what they consider to be “Idiomatic Go” and what isn’t – anything that isn’t idiomatic is normally wrong. Although I use semi-colons in JavaScript and it feels a little mean being told how to layout my code, it actually makes life a lot easier because every sample on the internet is the same, the code in the standard libraries acts and looks the same and it means you don’t end up with different coding standards and styles.

Go toolchain

go fmt

The command to build Go programs is “go build”. There are also other commands such as “go fmt” which takes your source code and re-formats it in the Go style. If we took this as an example …

package main;

import fmt;

func main() {

fmt.Println(Hello Simple Talk!);

}

… we can see that we have been a little frugal with whitespace and a little over the top with semi-colons. By running “go fmt” in the same directory, you will be able to re-format the code as:

package main

 

import fmt

 

func main() {

        fmt.Println(Hello Simple Talk!)

}

go vet

Code can also be validated at compile time to find common issues that were not found by the compiler, such as Printf calls that do not match the format string. I have personally seen many many issues in C# that were caused by null reference exceptions in String.Format or some variant.

If we change our sample code so it looks like this (note the missing value which should be passed into Printf after the format string) …

package main

 

import fmt

 

func main() {

        fmt.Printf(Hello Simple Talk! My name is %v\n)

}

https://play.golang.org/p/28WdebLPnc

…then running “go vet” in the folder gives us this error:

test.go:6: missing argument for Printf("%v"): format reads arg 1, have only 0 args

exit status 1

Although, in Go, this wouldn’t be a runtime error, it wouldn’t actually log a value. This would be very annoying to find out when you are debugging an issue and you see the text “Hello Simple Talk! My name is %!v(MISSING)” so it is nice to have tooling ‘out-of-the-box’ that can help stop these sort of silly errors.

IDEs

There are a number of IDEs that are available for Go, I am using Visual Studio Code with the Go extension but Jetbrains have an IDE and there are a few other ones as well:

Language Features of note

We will finish the first part of the introduction to Go for C# developers with a quick overview of a couple of features of Go which make developing a real pleasure.

Multiple return values

In Go, functions can return multiple values. This allows us to return both an object and a status for example; which means we can save a lot of the cruft that we get in other languages. If we look at an example of returning multiple values in C# we might create a struct to return the data:

class Something{

 

        public SomethingResult GoOn(){

 

                var result = new SomethingResult();

                if(itDidIt()){

                        result.SomeObject = new SomeObject();

                        result.Success = true;

                        return result;

                }

 

                result.Success = false;

                return result;

        }

}

To do the same thing in Go we could write it like:

        if itDidIt() {

                return true, new(someObject)

        }

 

        return false, nil

https://play.golang.org/p/x2R5ti9xWH

This leads to much easier to read code and avoids horrible alternatives in C# such as:

public void multipleReturnValuesAsOutArgs(int value, out bool status, out someObject someObject){

 

        

}

 

public void call it(){

 

        var status = false;

        someObject someObject = null;

        multipleReturnValuesAsOutArgs(999, out status, out someObject);

        if(status){

                //blah blah blah

        }

}

This doesn’t seem like a big deal, but when you are programming it allows you to quickly get on with the important and interesting thing if you can quickly return several different values from a function.

Garbage collection

The last language feature I will talk about today is the garbage collection. C# will let you create objects on the stack or the heap depending on the object type and garbage collection is quite expensive. In C#, value types such as ints, DateTimes or other structs are created on the stack and classes and other reference types are created on the heap; if you have a method that, in a tight loop, instantiates and then throws away significant amounts of objects, it will cause a lot of heap allocations and contention. This will make a garbage collection more likely. In Go, the compiler looks at how objects are used and, where possible, objects are created on the stack; this can make a massive difference to garbage collection. So even though Go is a garbage-collected langage like C#, this is not something to fear – the performance of it is awesome.

More Exciting GO

In the first part of this article, we showed you how to get setup with a build environment for GO and how to build a simple app. In this part we will look at doing something a little more exciting. We will demonstrate what Go is good for: a multi-threaded, scalable, networked service – an API that can be used as a replacement for http://isitlunchtimeyet.com/. We will show how to create a simple web application to serve the response to the critical question “Is It Lunchtime yet?”.

References

Each compiled go program includes its own compiled source code and, in addition, the compiled source code of every go dependency linked into the executable. To add a reference to a dependency you import the package that the dependency is in; so, for example, if you wanted to print out the current time, and you wanted to use:

fmt.Println(The time is now: , time.Now(), in UTC it is: , time.Now().UTC())

you would need to import the “time” and “fmt” packages. To do that, simply use an “import” statement or an “import” block:

import time

import fmt

… or the common syntax is:

import(

    fmt

    time

)

https://play.golang.org/p/HOSowhuefX

This means we can import any part of the GO standard library which is pretty vast:

https://golang.org/pkg/

There are other times, though, when we want to either share packages between projects internally or use somebody else’s package:  To do that, we use “go get” which:

  • Fetches a go package from a source control repository like github
  • Places the source code in a unique directory under the first GOPATH directory
  • Builds the source code and stores the output so it can be compiled into your project

“go get” can either be used to fetch any packages you have specified in your “import” directives or you can tell it what package to go and get as it is also used to download some useful tools. For example if we had an app that used the ‘gorilla’ package which is an http routing framework, we would just need to add the import to our main.go program and then run “go get” in the same directory as the main.go file

package main

 

import (

    github.com/gorilla/mux

)

 

func main() {

 

     mux.NewRouter().StrictSlash(true)

}

If we then changed to the directory and typed “go get” it would go off and get the code from github.com/gorilla/mux build it and we can use it in our code.

There is one thing to be aware of: When you create your directory, it needs to be under a directory that is specified in the environment variable for GOPATH, if you do not then you will see this error when running go get:

go install: no install location for directory “DIRECTORY” outside GOPATH
For more details see: go help gopath

If your GOPATH is set to “/home/user/go” or “c:\users\user\go” then add your directory under “/home/user/go/src/YourDirectory” or “C:\users\user\go\src\YourDirectory” and you will be fine.

There are plans to change this and move to more of a project model but as of writing go doesn’t support the new model yet. There are also some projects which have been created to make this simpler, a good example is https://getgb.io/. For more information on GOPATH, and why it is the way it is, see the blog post by Dave Cheney (https://dave.cheney.net/2016/12/20/thinking-about-gopath)

If we just wanted to do a go get without having a local file, then we can pass in the reference to go get such as:

go get github.com/gorilla/mux

Writing a simple http server

go includes, in its standard library, a fast web server that can serve http or https traffic. For our first example, we will respond to any URL that we receive with a simple text response:

package main

 

import (

        fmt

        log

        net/http

)

 

func main() {

 

        http.HandleFunc(/, func(w http.ResponseWriter, r *http.Request) {

                fmt.Fprint(w, Hello, Simple Talk\n)

        })

 

        log.Fatal(http.ListenAndServe(:8080, nil))

 

}

https://play.golang.org/p/umMqWwPxkj (These won’t actually run in the playground as it is a restricted/locked down version)

The important part here is:

    

    http.HandleFunc(/, func(w http.ResponseWriter, r *http.Request) {

               fmt.Fprint(w, Hello, Simple Talk\n)

        })

 

        log.Fatal(http.ListenAndServe(:8080, nil))

http.HandleFunc creates a handler for any URL that matches “/” – and then calls the anonymous function, we could also have split out the function and passed a pointer to it such as …

func main(){

    http.HandleFunc(/, rootHandler)

 

        log.Fatal(http.ListenAndServe(:8080, nil))

}

 

func rootHandler(w http.ResponseWriter, r *http.Request) {

               fmt.Fprint(w, Hello, Simple Talk\n)

}

… but an anonymous function is normally fine for a simple function like this.

The next line starts the http server (Listen and Serve) listening on port 8080. If it fails, it will cause the log package to throw a fatal error which, unless caught, will crash the app:

log.Fatal(http.ListenAndServe(:8080, nil))

If you send a web request to http://localhost:8080, you should see the message “Hello, Simple Talk”

If we expand on this slightly we can quickly see how easy it is to start to do something useful in go:

package main

 

import (

        fmt

        net/http

        time

)

 

func main() {

 

        http.HandleFunc(/isitlunchtimeyet, handleLunchTimeRequest)

        http.ListenAndServe(:8080, nil)

}

 

func handleLunchTimeRequest(res http.ResponseWriter, req *http.Request) {

 

        now := time.Now()

        if now.Hour() > 11 && now.Hour() < 14 {

                fmt.Fprintln(res, yes)

                return

        }

 

        fmt.Fprintln(res, no)

 

}

Here, when we are asked for the URL “/isitlunchtimeyet”, if it is between 11am and 2pm we say it is lunchtime, otherwise it isn’t.

This doesn’t initially look very scalable but what happens behind the scenes is for each new request a new goroutine is created. A goroutine is analogous to a thread in that each request happens independently and concurrently but is much more lightweight than an actual operating system thread so scaling multiple concurrent goroutines uses much less resources that scaling multiple threads.

Goroutine

To use a goroutine, we simply call a function and precede it with the word “go”:

func someThing(parameterOne string, parameterTwo int){

        fmt.Println(parameterOne: , parameterOne, parameterTwo: , parameterTwo)

}

 

func main(){

 

        fmt.Println(calling someThing)

        go someThing(aaaa, 1980)

        fmt.Println(done calling someThing)

}

https://play.golang.org/p/0u-_70PVLS

When this executes it will cause the someThing function to run in a separate goroutine to the main function. This will mean that the order of the fmt.Println will be, “calling someThing” followed by one of the other two Println statements, because a goroutine is analogous to a thread it isn’t possible to say exactly which one will come next but they will both be printed – also in this example because main is going to end which stops the program executing, it isn’t even possible to say whether the Println in someThing will even be called and in this specific example the main goroutine will likely exit before someThing gets a chance to run.

If we wanted to get the main function to wait for the someThing goroutine to finish we would need to use a “channel” to pass a message to tell the main goroutine that it is safe to finish. Channels are how goroutine’s communicate and going back to the original slogan from the first part of this article:

“Do not communicate by sharing memory; instead, share memory by communicating.”

In c# where we would need to lock an object or sharing a mutex or some other synchronization object between the two threads, we create a channel, tell the main goroutine to wait until we send it a message that it is ready. The full example is:

package main

 

import (

        fmt

)

 

func someThing(parameterOne string, parameterTwo int, exitChannel chan bool) {

        fmt.Println(parameterOne: , parameterOne, parameterTwo: , parameterTwo)

        exitChannel <- true

}

 

func main() {

 

        exitChannel := make(chan bool)

 

        fmt.Println(calling someThing)

        go someThing(aaaa, 1980, exitChannel)

        fmt.Println(done calling someThing)

 

        fmt.Println(waiting for stragglers to exit)

 

        <-exitChannel

 

        fmt.Println(and now (finally) exiting)

 

}

https://play.golang.org/p/0f5D7xvIxv

What we have here is we create the channel using:

        exitChannel := make(chan bool)

This means create a new variable called exitChannel and make it point to a new channel that accepts a single parameter of type bool – you can pass any type down a channel either a built-in type or your own type. We then have to change the definition of the someThing function to also take a channel as a parameter (we have to share some objects!).

func someThing(parameterOne string, parameterTwo int, exitChannel chan bool)

In the main function, when we have done everything we need to and we want to wait for the someThing goroutine, we use:

<- exitChannel

This means that we want to receive something from the channel. The arrows indicate the direction of flow, either to pull data from the channel or, in the case of the someThing function, when we are finished we push a value into the channel:

        exitChannel <- true

We could have just as easily passed in false for this because the main function isn’t actually looking at the value. If we wanted to store the value we received from the channel, we could do something along the lines of:

        result := exitChannel

        fmt.Println(the exit channel sent us the message: result)

https://play.golang.org/p/oSKUk3h4TZ

In our isitlunchtimeyet example, it would be quite good if we had a global counter to show how many people had asked whether it was lunch so we can expand our example to include a channel to send each request to so we can keep a count:

func main() {

 

        countingChannel := make(chan int)

        go keepCount(countingChannel)

 

        http.HandleFunc(/isitlunchtimeyet, handleLunchTimeRequest)

        http.ListenAndServe(:8080, nil)

}

 

func keepCount(countingChannel chan int) {

        

        currentCount = 0

        for elem := range countingChannel {

                currentCount++

        }

        

}

In the main function, we are creating a channel and calling keepCount as a goroutine. In keepCount we simply listen on the channel in a loop and when we get a message we  increment currentCount. We now need to get the handleLunchTimeRequestfunction to call the countingChannel. The handleLunchTimeRequest function needs to have a specific signature so that it is called when the /isitlunchtimeyet URL is called; so we can’t just add the channel as an additional parameter but what we can do is to convert the function into a method. In go, a function is similar to a static method in c#  in that it doesn’t store any state itself, whereas a method is a function that has what is called a receiver, a type that has state. If we look at this example of a type and a method we can see that we can use the receiver to store objects:

package main

 

import (

        fmt

        net/http

        time

)

 

type httpHandler struct {

        countingChannel chan int

}

 

func main() {

 

        var handler httpHandler

        handler.countingChannel = make(chan int)

        go keepCount(handler.countingChannel)

 

        http.HandleFunc(/isitlunchtimeyet, handler.handleLunchTimeRequest)

        http.ListenAndServe(:8080, nil)

}

 

func keepCount(countingChannel chan int) {

 

        currentCount := 0

        for _ = range countingChannel {

                currentCount++

                fmt.Println(currentCount)

        }

 

}

 

func (handler httpHandler) handleLunchTimeRequest(res http.ResponseWriter, req *http.Request) {

 

        now := time.Now()

 

        handler.countingChannel <- 1

 

        if now.Hour() > 11 && now.Hour() < 14 {

                fmt.Fprintln(res, yes)

                return

        }

 

        fmt.Fprintln(res, no)

 

}

https://play.golang.org/p/n4aA06mQOX

There are a few new things to point out here, the first is that we have created our own type:

type httpHandler struct {

        countingChannel chan int

}

There is a single field or property, “countingChannel” which is a channel that takes an int as a paramter. We then create a new instance of the httpHandler type with:

var handler httpHandler

This creates a new httpHandler: By default, everything in go is initialized to zero; so we then need to set the countingChannel to be a new channel:

handler.countingChannel = make(chan int)

Now, wherever we referenced the countingChannel, we change the references to the field on the handler object:

        go keepCount(handler.countingChannel)

We then change http.HandleFunc to call the method on our new handler instance:

http.HandleFunc(/isitlunchtimeyet, handler.handleLunchTimeRequest)

Our handleLunchTimeRequest function is then converted into a method by preceding the name with the receiver name and type: Think of a receiver as a this pointer:

func (handler httpHandler) handleLunchTimeRequest(res http.ResponseWriter, req *http.Request) {

We can then use the handler in the method to retrieve the correct instance of the channel:

        handler.countingChannel <- 1

Now when you call the URL, you should see the counter being incremented. The important thing here is that we have used no locks or synchronization methods, no ReaderWriterLockSlim‘s but we guarantee that we can serve requests concurrently and we:

  1. do not block the handler goroutine
  2. do not miss any counts

If we launched our version of isitlunchtimeyet we would undoubtable be swamped with requests for additional features, and the top one would likely be to support different meal types so we would want to allow the users to pass in the URL both the meal type. To do that, I would be inclined to make use of a different http router than the default, and one of the most popular ones is gorilla. The system it has of allowing parameters to be included in URLs is really great:

package main

 

import (

        fmt

        net/http

        time

 

        strings

 

        github.com/gorilla/mux

)

 

type httpHandler struct {

        countingChannel chan int

}

 

func main() {

 

        var handler httpHandler

        handler.countingChannel = make(chan int)

        go keepCount(handler.countingChannel)

 

        router := mux.NewRouter()

        router.HandleFunc(/isit{meal}timeyet/, handler.handleMealTimeRequest)

        

        http.ListenAndServe(:8080, router)

}

 

func keepCount(countingChannel chan int) {

 

        currentCount := 0

        for _ = range countingChannel {

                currentCount++

                fmt.Println(currentCount)

        }

}

 

func (handler httpHandler) handleMealTimeRequest(res http.ResponseWriter, req *http.Request) {

 

        vars := mux.Vars(req)

        meal := vars[meal]

 

        now := time.Now()

 

        handler.countingChannel <- 1

        switch strings.ToLower(meal) {

        case lunch:

               if isItLunchTimeYet(now) {

                      fmt.Fprintln(res, yes)

                      return

                }

        case breakfast:

               if isItBreakfastTimeYet(now) {

                      fmt.Fprintln(res, yes)

                      return

                }

        case dinner:

               if isItDinnerTimeYet(now) {

                      fmt.Fprintln(res, yes)

                      return

                }

        default:

                fmt.Fprintf(res, What is %v?\n, meal)

               res.WriteHeader(http.StatusTeapot)

                return

        }

 

        fmt.Fprintln(res, no)

 

}

 

func isItLunchTimeYet(t time.Time) bool {

        return t.Hour() > 11 && t.Hour() < 14

}

 

func isItBreakfastTimeYet(t time.Time) bool {

        return t.Hour() >= 6 && t.Hour() < 10

}

 

func isItDinnerTimeYet(t time.Time) bool {

        return t.Hour() > 16 && t.Hour() < 20

}

The interesting parts of this are that we now use the gorilla routing instead of the default routing:

        router := mux.NewRouter()

        router.HandleFunc(/isit{meal}timeyet/, handler.handleMealTimeRequest)

        

        http.ListenAndServe(:8080, router)

This means that we are looking for two URLs, the first is “/isit” then any word and then “timeyet” – this lets us accept lunch, breakfast or anything – we need to be aware of that when we read the variable from the URL:

func (handler httpHandler) handleMealTimeRequest(res http.ResponseWriter, req *http.Request) {

 

        vars := mux.Vars(req)

        meal := vars[meal]

“meal” is the word that is between “/isit” and “timeyet” – we then simply have a switch to let us choose which set of timings to ask for. If we don’t understand what the meal word was then we will fall to the default case and can return an appropriate error message.

Conclusion

I think that you can pretty easily build up an API or web service to do something useful, there are packages that let you connect to SQL Server and pretty much every other data source that a c# developer could be used to already. Go isn’t perfect for everything; HTML templating for example isn’t great, but a good approach to building web sites is to use go to run the API layer and drive the the front end with node.js or something similar: And if simplicity and scale is what you’re after,  go is a language worth investigating.

References

  1. https://golang.org/
  2. https://github.com/golang/go/wiki
  3. https://blog.golang.org/

How you log in to Simple Talk has changed

We now use Redgate ID (RGID). If you already have an RGID, we’ll try to match it to your account. If not, we’ll create one for you and connect it.

This won’t sign you up to anything or add you to any mailing lists. You can see our full privacy policy here.

Continue

Simple Talk now uses Redgate ID

If you already have a Redgate ID (RGID), sign in using your existing RGID credentials. If not, you can create one on the next screen.

This won’t sign you up to anything or add you to any mailing lists. You can see our full privacy policy here.

Continue