Andrei Dumitrescu

Google has created the Go programming language (referred to as Golang) to solve “Google-size” problems. And it has a strong chance of becoming the next enterprise programming language.

In a nutshell, Golang provides high performance (like C and C++) and super-efficient concurrency handling (like Java), while also being fun to code (like Python). 

According to the StackOverflow survey for 2020, Golang is the second-highest paying technology in the USA and around the world. As more and more companies are using Golang, the demand for professionals in the area is growing fast. 

Let’s take a look at some of the most common Golang programming interview questions, as well as how to best respond to these questions in an idiomatic way.

Master Go (Golang) Programming:The Complete Go Bootcamp 2021

Last Updated January 2021

  • 209 lectures
  • All Levels
4.4 (403)

Google’s Go (Golang) from Beginner to Pro. Become a Professional Golang Programmer from Scratch.With Hands-On Exercises! | By Andrei Dumitrescu, Crystal Mind Academy

Explore Course

Question 1: How can you format the Go source code in an idiomatic way?

A: The Golang standards do not insist on a coding style as Python does, for example. Instead, the standards do recommend certain styles.  

gofmt (golang formatter) will format a program’s source code in an idiomatic way. This makes it easy to read and understand. This tool is built into the language runtime, and it formats the Go source code according to a set of stable, well-understood language rules. 

We can run it by typing the command at the command line or we can configure the IDE to run gofmt each time we save a file.

For example, gofmt -w main.go will format the source file main.go according to Go Programming Language style recommendations. The -w option writes the results back to the source file.

If we want to run gofmt on all files in a specific directory, we run: gofmt -w -l directory/

You can also run the go command with the fmt argument. It will execute goftm -l -w behind the scenes and format all source code in each file in the current working directory.

Question 2: You have developed a Go program on Linux and want to compile it for both Windows and Mac. Is it possible?

A: Yes, it’s possible to compile a Go application for different operating systems.

Question 3: How can you compile a Go program for Windows and Mac?

A: To compile the program, move to the application’s directory. Then execute the following commands in the terminal.

Compile the application for Windows and 64bit CPUs:

GOOS=windows GOARCH=amd64 go build -o my_go_program.exe

Compile the application for Mac and 64bit CPUs:

GOOS=darwin GOARCH=amd64 go build -o  my_go_program

Note that for Linux and Mac it’s not necessary to use a file’s extension.

Question 4: Starting from an empty file, how would you create a Go program’s basic structure? Annotate the most important parts of the source code using comments.

A:

Source Code.

// a package clause starts every Go source file

// main is a special name declaring an executable rather than a library (package)

package main

// import declaration declares packages used in this file

// fmt is a standard library package used to print to standard output (console)

import “fmt”

// function declaration. main is a special function name

// it’s the entry point for the executable program

func main() {
	// local scoped variables and constants declarations, calling functions etc
	var age int = 20
	var today string = "Monday"
 
	// Println() function from fmt package prints out a line to stdout
	fmt.Println("Today is", today) // => Today is Monday
	fmt.Println("Your age is", age) // => Your age is 17
 
}

Question 5: What is the string data type in Golang, and how is it represented?

A: A string is a series of byte values. It’s a slice of bytes, and any byte slice can be encoded in a string value. So we can encode anything in a string, even beyond just Unicode text, like images or binary applications.

Golang doesn’t have a char data type. It uses bytes and runes to represent character values.

Question 6: Explain byte and rune types and how they are represented.

A: Golang has two integer types called byte and rune that are aliases for uint8 and int32 data types. 

The byte data type represents ASCII characters, and the rune data type represents Unicode characters that are encoded in the UTF-8 format by default.

In Golang, we express characters or rune literals by enclosing them in single quotes such as ‘a,’ ‘b,’ ‘m,’ ‘x,’ or ‘\n.’ A code point is a numeric value that represents a rune literal. 

The Golang terminology for code points is runes (rune is a term introduced in Golang specification for code point). Most of the time, we say “rune” instead of “code point.” A rune represents a single Unicode character.  For example, rune 0x61 in hexadecimal represents the rune literal ‘a.’

Question 7: Can you change a specific character in a string?

A: No. Strings are immutable (read-only) data types and you cannot change them. If we try to change a specific character in a string, we’ll get a runtime error.

Question 8: How can you concatenate string values? What happens when concatenating strings?

A: To concatenate strings, we can use the addition operator (+). Note that each time we concatenate to a string value, Go creates a new string. That’s because strings are immutable in Go, making this inefficient.

There is an efficient and recommended way to concatenate string values and that is to use the strings.Builder type, which was introduced in Go 1.10.

Question 9:  Explain array and slice types and the differences between them.

A: Golang has two data structures to handle lists of records: arrays and slices.

An array is a composite, indexable type that stores a collection of elements. 

An array has a fixed length. We specify how many items are in the array when we declare it. This is in contrast to a slice that has a dynamic length (it can shrink or grow at runtime). 

The array length is part of its type.

Every element in an array or slice must be of the same type.

Slices are a key data type in Golang and are everywhere.

Question 10: Give an example of an array and slice declaration.

A: Here’s an example of declaring and initializing an array of type [4] string using the short declaration syntax.

friends := [4]string{“Dan”, “Diana”, “Paul”, “John”} 

Here’s an example of declaring and initializing a slice of type [] int using the short declaration syntax.

numbers := []int{2, 3, 4, 5}

Question 11: Explain the backing array of a slice value.

A: When we create a slice, Go creates a hidden array behind the scenes, called backing or underlying array, and the new slice type variable refers to it. The backing array stores the elements, not the slice. 

Go implements a slice as a data structure called slice header, which is the runtime representation of the slice.

It contains three fields:

  1. The address of the backing array (a pointer to the first element of the backing array).
  2. The length of the slice. The built-in function len() returns it.
  3. The capacity of the slice which is the size of the backing array after the first element of the slice. It’s returned by the cap() built-in function.

Note that a nil slice doesn’t have a backing array, so all the fields in the slice header are equal to zero.

Question 12:  What will the following Go program print out?

package main
import "fmt"
 
func main() {
    n1 := []int{10, 20, 30, 40}
    n1 = append(n1, 100)
    fmt.Println(len(n1), cap(n1))
}

A: The program will print out 5 8 

The append() function creates a new backing array with a larger capacity. This avoids creating a new backing array when the next append() is called.

Question 13: Explain the Golang map type and its advantages.

A: A map is a collection type like an array or a slice and stores key:value pairs. We can think of a map being like a dict in Python or an Object in JS.

All the keys and values in a map are statically typed and must have the same type. Keys and values don’t have to be of the exact type, but all keys must be of the same type, and all values in the map must be of the same type. For example, all keys are of type string and all values are of type int.

We can use any comparable type as a map key. A comparable type is that type that supports the comparison operator. This is the double equals sign (==).

The main advantage of maps is that add, get, and delete operations take constant expected time no matter how many entries are in the map. They offer very fast lookups because maps are backed by HashTables. 

Maps are unordered data structures in Golang. 

Question 14:  What is the recommended Golang package for basic operations on files? What other Golang packages are used to work with files?

A: The os standard library package provides a platform-independent interface. We use it for system functionality when working with files. 

The os interface is intended to be uniform across all operating systems. So the programs we create work the same on Windows, Linux, or Mac.

There are other Go standard library packages, such as io, ioutil, and bufio. They work with files and provide more functionality.

However, for basic operations on files, they are not necessary. os package is all we need.

Question 15: Explain the Object-Oriented Architecture of Golang.

A: Unlike traditional Object-Oriented Programming, Golang does not have a class-object architecture. Rather structs and methods hold complex data structures and behavior.

A struct is nothing more than a schema containing a blueprint of data that a structure will hold. Structs are useful to represent concepts from the real world like cars, people, or books.

Question 16: What is a struct type? Can you change the struct definition at runtime?

A: A struct is a sequence of named elements called fields. Each field has a name and a type. We can also think of a struct as a collection of properties that are related together. They are useful for grouping data together to form records.

This blueprint is fixed at compile time. It’s not allowed to change the name or the type of the fields at runtime. We can’t add or remove fields from a struct at runtime. 

Question 17: What are anonymous structs and anonymous struct fields? Give an example of such a struct declaration.

A: An anonymous struct is a struct with no explicitly defined struct type alias.

Example of an anonymous struct:

diana := struct {
       firstName, lastName string
       age                 int
   }{
       firstName: "Diana",
       lastName:  "Zimmermann",
       age:       30,
   }

We can define a struct type without any field names. We call them anonymous fields. All we have to do is mention the field types and Go will use type as a field name.

Example of a struct with anonymous fields:

type Book struct {
       string
       float64
       bool
   }
bestBook := Book{"1984 by George Orwell", 10.2, false}

Question 18: Explain the defer statement in Golang. Give an example of a deferred function’s call.

A: A defer statement defers or postpones the execution of a function. It postpones the execution until the surrounding function returns, either normally or through a panic call.

We use defer to ensure that a function call is performed later in the program’s execution, usually for cleaning resources.

For example, let’s say that we want to create a file, write to it, and then close when we’re done with it.

Immediately after creating the file variable, we defer the closing of that file. The function that closes the file will be executed at the end of the enclosing function (main) after the operation of writing to the file has finished.

   file, err := os.Open("main.go")
   if err != nil {
       log.Fatal(err)
   }
   defer file.Close()

Question 19: Explain the pointer type.

A: A variable is a convenient, alphanumeric nickname or label for a memory location.  A pointer is a variable type that stores the memory address of another variable. 

A pointer value is the address of a variable, or nil if it hasn’t been initialized yet.

The pointer points to the memory address of a variable, just as a variable represents the memory address of a value.

For example, if variable bb has value 156 and is stored at memory address 0x1040a124 then the variable aa holds the address of bb (0x1040a124). Now aa is set to point to bb or aa is a pointer to bb

Question 20: Write a Golang program that declares a string variable, prints the address of the variable, declares another int variable, and a pointer to it.

A: 

Source code.

package main

import “fmt”

func main() {

	//Go provides the & (ampersand) operator also known as the address of operator.
	lang := "Golang"
	fmt.Println(&lang) // -> 0xc00010a040

	var x int = 2                                                                  // -> int value
	ptr := &x                                                                      // -> pointer to int
	fmt.Printf("ptr is of type %T with value %v and address %p\n", ptr, ptr, &ptr) 
}

Question 21: What are the advantages of passing pointers to functions?

A: Golang is a pass by value language. 

If we pass a non-pointer variable type to a function, the function will create a copy of the variable. Any change to the variable, so to the function’s argument, will not be seen to the outside world.

Pointers have the power to mutate or change the data they are pointing to. So if we pass a pointer to a function, and inside the function we change the value the pointer is pointing to, then the change will be seen outside the function. 

In a nutshell, we pass pointers to functions when we want to change the values of the variables inside the function’s body.

Question 22: What are Golang methods? 

A: Golang doesn’t have classes, but we can define methods on defined types. A type may have a method set associated with it which enhances the type with extra behavior.

This way a named type has both data and behavior, and represents better a real-world concept.

Methods are also known as receiver functions.

Question 23: Create a Go program that defines a named type and a method (receiver function) for that type.

A:

Source code.

package main

import (
	"fmt"
)
 
// declaring a new type
type names []string
 
// declaring a method (function receiver)
func (n names) print() {
	// n is called method's receiver
	// n is the actual copy of the names we're working with and is available in the function.
	// n is like this or self from OOP.
	// any variable of type names can call this function on itself like variable_name.print()
 
	// iterating and printing all names
	for i, name := range n {
		fmt.Println(i, name)
	}
}
 
func main() {
 
	// declaring a value of type names
	friends := names{"Dan", "Marry"}
	// calling the print() method on friends variable
	friends.print()
 
	// another way to call a method on a named type
	names.print(friends)
}

Question 24: What is a goroutine? Go deeper into it.

A: A goroutine is a function that is capable of running concurrently with other functions. It’s a lightweight thread of execution.

When a Go program is running, the main goroutine is created and launched. Other goroutines are created using the go keyword. So any function that is called using the go keyword before its name becomes a goroutine.

The difference between OS threads and goroutines is that OS threads are scheduled by the OS kernel. And this is a slow operation due to the amount of memory access required.  

The Go runtime contains its scheduler to schedule goroutines.

A goroutine is very cheap. It’s practical to have thousands, even hundreds of thousands, of goroutines.  

Question 25: Explain why concurrency is not parallelism?

A: Concurrency means loading more goroutines at a time. These goroutines are multiple threads of execution. If one goroutine blocks, another one is picked up and started. On a single-core CPU, we can run only concurrent applications, but they are not run in parallel. They run sequentially. 

On the other hand, parallelism means multiple goroutines are executed at the same time. It requires multiple CPUs.

Concurrency and parallelism are related but distinct concepts. Concurrency means independently executing processes or dealing with multiple things at once, while parallelism is the simultaneous execution of processes and requires multiple core CPUs.

Question 26: What is a data race?

A: Executing many goroutines at the same time without special handling can introduce an error called “Race Condition” or “Data Race.” 

A Data Race occurs when two goroutines are accessing memory at the same time, one of which is writing. Race conditions occur because of unsynchronized access to shared memory. They are among the most common and hardest to debug types of bugs in concurrent systems. 

Question 27: How could you detect a data race in Go code?

A: Starting with Go 1.1, a new tool called race detector for finding race conditions in Go code was made available.  

Using the race detector is simple. We just add a -race flag to our normal Go command-line tool.

When the -race command-line flag is set, the compiler inspects all memory accesses with code that records when and how the memory was accessed. In the meantime,  the runtime library watches for unsynchronized access to shared variables. 

Example of running a Go program with the race detector: go run -race main.go

Question 28: What is Go Channel? What operations are available on the channel type?

A: A channel in Go provides a connection between two goroutines, allowing them to communicate.

The data we are sending through or receiving from a channel must always be of the same type. This is the type specified when we’ve created the channel.

Question 29: What operations are available on the channel type?

A: A channel is a 2-way messaging object that has two principal operations: send and receive.

A send statement transmits a value from one goroutine to another goroutine.  It executes a corresponding receive expression. The transmission goes through the channel. Both operations are written using the channel operator(<-).

Channels are used to communicate in between running goroutines.

Question 30: Write a simple Golang program that uses a goroutine and a channel.

A:

Source code.

package main

import “fmt”

func func1(n int, ch chan int) {
	//sending into the channel
	ch <- n
}
 
func main() {
	// declaring a bidirectional channel that transports data of type int
	c := make(chan int)
	fmt.Printf("%T\n", c)
 
	// starting the goroutine
	go func1(10, c)
 
	//receiving data from the channel
	n := <-c
	fmt.Println("Value received:", n)
 
	fmt.Println("Exiting main ...")
}

Final Thoughts

I hope you’ll find these interview questions useful. The answers to them and many other questions can be found in my course Master Go (Golang) Programming: The Complete Go Bootcamp. The course covers the Go programming language in depth.

Keep in mind that the only way to learn how to code is to write code so exercise as much as possible until you feel confident in your abilities as a Golang programmer.

Page Last Updated: November 2020

Go Programming Language students also learn

Empower your team. Lead the industry.

Get a subscription to a library of online courses and digital learning tools for your organization with Udemy for Business.

Request a demo

Courses by Andrei Dumitrescu

Master Go (Golang) Programming:The Complete Go Bootcamp 2021
Andrei Dumitrescu, Crystal Mind Academy
4.4 (403)
Master Python Programming: The Complete Python Bootcamp 2021
Andrei Dumitrescu, Crystal Mind Academy
4.7 (589)
Master Network Automation with Python for Network Engineers
Andrei Dumitrescu, Crystal Mind Academy
4.6 (1,477)
Bestseller
Linux Security: The Complete Iptables Firewall Guide
Andrei Dumitrescu, Crystal Mind Academy
4.6 (555)
Master Ethereum & Solidity Programming:Build Real-World Apps
Andrei Dumitrescu, Crystal Mind Academy
4.3 (439)
Ethical Hacking and Network Security from Scratch 2021
Andrei Dumitrescu, Crystal Mind Academy
4.7 (157)
Linux Administration: The Complete Linux Bootcamp 2021
Andrei Dumitrescu, Crystal Mind Academy
4.6 (145)
Bestseller

Courses by Andrei Dumitrescu