Introduction If you are new to Linux or the Mac you might find installing Go to be a bit confusing. It was for me when I started learning Go. Go was the reason I stopped using Windows, which I used for 20 years. Even if you’re experienced with these operation systems, setting up Go might seem a bit of a mystery. With this in mind, let’s walk through installing Go.
I am constantly thinking about the Go language and how things work. Lately I have been thinking how everything in Go is by value. We see this when we pass values to functions, when we iterate over slices and when we perform type assertions. In every case, copies of the values that are stored in these data structures are returned. When I first started learning Go this threw me off, but I have come to appreciate the reasonability this brings to our code.
Composition goes beyond the mechanics of type embedding. It’s a paradigm we can leverage to design better APIs and to build larger programs from smaller parts. It all starts from the declaration and implementation of types that have a single purpose. Programs that are architected with composition in mind have a better chance to grow and adapt to changing needs. They are also much easier to read and reason about.
Go is an object oriented programming language. It may not have inheritance, but in this 20 minute video from the Bangalore meetup, I will show you how object oriented programming practices and techniques can be applied to your Go programs. From an object oriented standpoint, Go does provides the ability to add behavior to your types via methods, allows you to implement polymorphic behavior via interfaces and gives you a way to extend the state and behavior of any existing type via type embedding.
Introduction One of the things I love about Go is the profiling and debug information you can generate. There is a special environmental variable named GODEBUG that will emit debugging information about the runtime as your program executes. You can request summary and detailed information for both the garbage collector and the scheduler. What’s great is you don’t need to build your program with any special switches for it to work.
Having some basic skills in debugging Go programs can save any programmer a good amount of time trying to identify problems. I believe in logging as much information as you can, but sometimes a panic occurs and what you logged is not enough. Understanding the information in a stack trace can sometimes mean the difference between finding the bug now or needing to add more logging and waiting for it to happen again.
I am asked quite a bit about when and when not to use pointers in Go. The problem most people have, is that they try to make this decision based on what they think the performance tradeoff will be. Hence the problem, don’t make coding decisions based on unfounded thoughts you may have about performance. Make coding decisions based on the code being idiomatic, simple, readable and reasonable.
My use of pointers is based on discoveries I have made looking at code from the standard library.
In part I of this post, we learned about the error interface and how the standard library provides support for creating error interface values via the errors package. We also learned how to work with error interface values and use them to identify when an error has occured. Finally, we saw how some packages in the standard library export error interface variables to help us identify specific errors.
Knowing when to create and use custom error types in Go can sometimes be confusing.
It is idiomatic in Go to use the error interface type as the return type for any error that is going to be returned from a function or method. This interface is used by all the functions and methods in the standard library that return errors. For example, here is the declaration for the Get method from the http package: Listing 1.1
func (c *Client) Get(url string) (resp *Response, err error) Listing 1.
I was thinking about how the compiler looks to protect the code we write when it can. Invalid memory access checks are one type of safety check the compiler adds to our code. We might think that this "extra code" is hurting our performance and maybe over billions of iterative operations it is. However, these checks can prevent our code from causing damage to the systems we are running on.