Go long with Golang

Go long with Golang

What's so unique about Golang syntax?

What's so unique about Golang syntax?

Golang is pretty simple to read and write, but when you first see some code snippets of it, some of it’s syntax looks a bit weird. However, once you get acquainted with how it works, you’ll realize how neat this syntax actually is.

I’d like to go over some syntax that you will find in the Go programming language, that you probably won’t see in other popular languages.

1. Implicit type declaration

Since Go is statically typed, you need to declare the type of the variable when defining it. So, if you want to create a variable called city of type string with the value "Miami", you write:

var city string = "Miami"

Pretty simple right? But , when reading Go code, you usually won't see it like this. What you will see more often is :

city := "Miami"

The := operator (also known in Python as the walrus operator) is used for type inference so that you don't have to declare the type, since it is pretty obvious from it's value that it is a string.

This isn't something Go has invented. Java and C# have implemented this long ago, but instead of using := , they both use the var keyword. This way of using type inference is pretty unique.

2. For loops used instead of while

This looks kind of weird the first time you see it. Let's say you need an endless loop . In most programming languages you would write something like : while true { ... } . Not in Go however. Go actually doesn't even recognize the while keyword. The reason for this is that the creators of Go really wanted to keep it as minimal as possible . As a result, Go has only 25 keywords ( Java has 61 ) .

So how do you create an endless loop in Go? One word - for . Want your terminal to write "Hello" endlessly? ( obviously not the cleverest reason for using and endless loop ) Here's the syntax :

for {
        fmt.Println("Hello")
    }

When you think about it, for is predominantly used for iterating over collections, someting you will be doing a lot as a developer. while however, is usually used much less often, so why not just merge the two keywords into one.

3. Error handling

You are writing a program that needs to call a third party API. You are aware that your program now depends not only on your code, but on some server god knows where being maintained by god knows who. To make sure your app doesn't crash, you'd better put the code that sends the HTTP request in a try/catch block (or try/except for my fellow Pythonistas) . Well, Golang is not going to be cooperative about that.

You see, there is no try/catch in Go.

Wait, so Go apps crash all the time, right?

Nope.

Go has (like many languages) the possibility of returning multiple values from a function call. Instead of writing try/catch , Go enables the developer to check if one of the returned values is an error. Let's see how this looks in practice:

resp, err := http.Get("https://someExternalApi.com")
if err != nil {
   log.Fatalln(err)
}

You will be seeing a whole lot of this in Go. I remember when I first saw a large Go code base, it was full of these if err !=nil snippets. It felt like literally 80% of the code. Well, that's how you handle errors in Go. And, it's really useful, because it forces you to check for errors.

Forces me? What are you talking about?

If you don't believe me try this:

resp := http.Get("https://someExternalApi.com")

The Go compiler will refuse to run it. The http.Get method retuns two values , the latter being the error. If you don't define two values, the compiler will say someting like : cannot initialize 1 variables with 2 values

Ok, so what? You will define the error , but how does that force you to actually do anything with it? Well, in Golang, defining a variable and not using it is impossible.

If you write :

resp,err := http.Get("https://someExternalApi.com")
fmt.Println(resp.Status) // this will just print out the response status code

the compiler will say : err declared but not used

The reason for this is that the Go compiler is incredibly pragmatic. It doesn't like monkey business like unused imports or variables, because these will make the compiler seem slow and the binaries that it produces will seem bloated. The speed at which the Go compiler produces it's lean binaries is truly a thing of beauty.

Now, if you are really stubborn, and just don't want to handle the damn error, you can do something like this:

resp, _ := http.Get("https://someExternalApi.com")
fmt.Println(resp.Status)

The _ character tells the compiler that you are aware that this function returns two values but you only want to use the first one. At this point the compiler gives up on lecturing you about error handling, but when your app crashes , at least you can't say that it didn't do it's best to warn you.

4. The gorountine

Goroutines are a topic that deserve it's own article, which I will definitely be writing in the future. For now, I just want you to get acquainted with the syntax so you don't get confused when you see it.

Let's say we are going to be calling a lot of different external APIs in our app. Logically, we want to wrap the above code in a function, so we don't need to rewrite every line for every API call. Let's do that first:

func getApiResponse(url string) {
    resp, err := http.Get(url)
    if err != nil {
        log.Fatal(err)  // notice how I'm being responsible about error handling :)
    }
    fmt.Println(resp.Status) 
}

Great. But, wait. My app sometimes needs to send 500 API calls to get some information. How do I do this. Maybe this? :

urls := []string{"url1","url2","url3"} //this is a slice of strings containing all the urls
for _, url := range urls { 
        getApiResponse(url)
        }

( here I am using _ because the first return value of the range is an index, which I really do not need )

Well this would be ok , right?

Well, not really. The problem with this approach is that the requests will be sent synchronously , meaning one after the other. This can take quite a while. What you want is a way to send them concurrently. Enter gorountines. Like I said , explaining goroutines in detail will go into it's own article, but for now here is the syntax:

urls := []string{"url1","url2","url3"} 
for _, url := range urls {
        go getApiResponse(url)
        }

That's it! You just add the go keyword if front of your function call, and watch the execution time drop enormously.

One thing to note if you actually run this snippet :

(actually two things - the first being that if you really want a response , enter valid urls instead of the "url1","url2" placeholders )

You won't actually see the response, because the main function will finish execution before to goroutines are done. This is where it gets a bit complex, and explaining it fully would require introducing the concept of channels . I won't do this right now, but if you really want to see a return value immediately you can do this:

urls := []string{"url1","url2","url3"}
for _, url := range urls { 
        go getApiResponse(url)
        time.Sleep(time.Millisecond * 100)
    }

This will make the main function wait for 100 milliseconds, thus giving the goroutine time to return the value before the main function exits. This usually isn't the way you would do it in a real app, but for the sake of simplicity , it will do for now.

That's all for this article, I hope you learned something new.

Thanks for reading, and see you in the next one!

 
Share this