Go interview questions

Go quiz questions

  • 1.

    Do you need both GOPATH and GOROOT variables, and why?

    Answer:

    Most of the time, you do not need them both. You need only the GOPATH variable set pointing to the Go packages tree or trees.

    GOROOT points to the root of the Go language home directory, but it is most probably already set to the directory of the current Go language installation. It is easy to check whether it is so with the go env command:

    $ go env
    …
    GOROOT=“/home/zabb/go”
    …
    

    It is necessary to set the GOROOT variable if there are multiple Go language versions on the same system or if the Go language has been downloaded as a binary package taken from the internet or transferred from another system.

    View
  • 2.

    What is the difference, if any, in the following two slice declarations, and which one is more preferable?

    var a []int
    

    and

    a := []int{}

    Answer:

    The first declaration does not allocate memory if the slice is not used, so this declaration method is preferred.

    View
  • 3.

    What would you do if you need a hash displayed in a fixed order?

    Answer:

    You would need to sort that hash’s keys.

    fruits := map[string]int{
    	"oranges": 100,
    	"apples":  200,
    	"bananas": 300,
    }
    // Put the keys in a slice and sort it.
    var keys []string
    for key := range fruits {
    	keys = append(keys, key)
    }
    sort.Strings(keys)
    // Display keys according to the sorted slice.
    for _, key := range keys {
    	fmt.Printf("%s:%v\n", key, fruits[key])
    }
    // Output:
    // apples:200
    // bananas:300
    // oranges:100
    View
  • 4.

    What might be wrong with the following small program?

    func main() {
    	scanner := bufio.NewScanner(strings.NewReader(`one
    two
    three
    four
    `))
    	var (
    		text string
    		n    int
    	)
    	for scanner.Scan() {
    		n++
    		text += fmt.Sprintf("%d. %s\n", n, scanner.Text())
    	}
    	fmt.Print(text)
    	// Output:
    	// 1. One
    	// 2. Two
    	// 3. Three
    	// 4. Four
    }
    

    The program numbers the lines in a buffer and uses the text/scanner to read the input line-by-line. What might be wrong with it?

    Answer:

    First, it is not necessary to collect the input in the string before putting it out to standard output. This example is slightly contrived.

    Second, the string text is not modified with the += operator, it is created anew for every line. This is a significant difference between strings and []byte slices — strings in Go are non-modifiable. If you need to modify a string, use a []byte slice.

    Here’s a provided small program, written in a better way:

    func main() {
    	scanner := bufio.NewScanner(strings.NewReader(`one
    two
    three
    four
    `))
    	var (
    		text []byte
    		n    int
    	)
    	for scanner.Scan() {
    		n++
    		text = append(text, fmt.Sprintf("%d. %s\n", n, scanner.Text())...)
    	}
    	os.Stdout.Write(text)
    	// 1. One
    	// 2. Two
    	// 3. Three
    	// 4. Four
    }
    

    That is the point of the existence of both bytes and strings packages.

    View
  • 5.

    How would you implement a stack and a queue in Go? Explain and provide code examples.

    Answer:

    You use slices to implement a stack or queue by yourself:

    type Stack []int
    func (s Stack) Empty() bool { return len(s) == 0 }
    func (s *Stack) Push(v int) { (*s) = append((*s), v) }
    func (s *Stack) Pop() int {
    	v := (*s)[len(*s)-1]
    	(*s) = (*s)[:len(*s)-1]
    	return v
    }
    type Queue []int
    func (q Queue) Empty() bool    { return len(q) == 0 }
    func (q *Queue) Enqueue(v int) { (*q) = append((*q), v) }
    func (q *Queue) Dequeue() int {
    	v := (*q)[0]
    	(*q) = (*q)[1:len(*q)]
    	return v
    }
    func main() {
    	s := Stack{}
    	s.Push(1)
    	s.Push(2)
    	fmt.Println(s.Pop())
    	fmt.Println(s.Pop())
    	fmt.Println(s.Empty())
    	// Output:
    	// 2
    	// 1
    	// true
    	q := Queue{}
    	q.Enqueue(1)
    	q.Enqueue(2)
    	fmt.Println(q.Dequeue())
    	fmt.Println(q.Dequeue())
    	fmt.Println(q.Empty())
    	// Output:
    	// 1
    	// 2
    	// true
    }
    

    The queue implementation above is correct, but it is suboptimal. There are better but lengthier implementations, like this one.

    Occasionally, you would prefer the Go standard library’s container/list to implement them for their conciseness, genericity, and extra list data structure related operations:

    stack := list.New()
    stack.PushBack(1)
    stack.PushBack(2)
    fmt.Println(stack.Remove(stack.Back()))
    fmt.Println(stack.Remove(stack.Back()))
    fmt.Println(stack.Len() == 0)
    // Output:
    // 2
    // 1
    // true
    queue := list.New()
    queue.PushBack(1)
    queue.PushBack(2)
    fmt.Println(queue.Remove(queue.Front()))
    fmt.Println(queue.Remove(queue.Front()))
    fmt.Println(queue.Len() == 0)
    // Output:
    // 1
    // 2
    // true
    

    Although, their usage is generally discouraged for their slower performance, compared to slices iteration pattern. Let’s compare the two following examples:

    // Iterate through a list and print its contents.
    for e := queue.Front(); e != nil; e = e.Next() {
        fmt.Println(e.Value)
    }
    
    for _, e := range queue {
        fmt.Println(e)
    }
    

    “Always use a slice.”, Dave Cheney

    Another possibility to implement a queue is to use buffered channels, but this is never a good idea, because:

    1. The buffer size is determined at the channel creation and cannot be increased.
    2. It is impossible to peek at the next queue element without retrieving it from the queue.
    3. There is a risk of deadlock: “Novices are sometimes tempted to use buffered channels within a single goroutine as a queue, lured by their pleasingly simple syntax, but this is a mistake. Channels are deeply connected to goroutine scheduling, and without another goroutine receiving from the channel, a sender—and perhaps the whole program—risks becoming blocked forever. If all you need is a simple queue, make one using a slice.”, Brian Kernighan.
    View
  • 6.

    What is wrong with the following code snippet?

    type Orange struct {
    	Quantity int
    }
    func (o *Orange) Increase(n int) {
    	o.Quantity += n
    }
    func (o *Orange) Decrease(n int) {
    	o.Quantity -= n
    }
    func (o *Orange) String() string {
    	return fmt.Sprintf("%v", o.Quantity)
    }
    func main() {
    	var orange Orange
    	orange.Increase(10)
    	orange.Decrease(5)
    	fmt.Println(orange)
    }
    

    Provide the proper code solution.

    Answer:

    This is a trick question because you might think this has something to do with the member variable Quantity being set incorrectly, but actually, it will be set to 5 as expected. The real problem here, which is easy to overlook, is that the String() method that implements the fmt.Stringer() interface will not be invoked when the object orange is being printed with fmt.Println() function, because the method String() is not being defined on a value but only on a pointer:

    var orange Orange
    orange.Increase(10)
    orange.Decrease(5)
    fmt.Println(orange)
    // Output: {5}
    orange := &Orange{}
    orange.Increase(10)
    orange.Decrease(5)
    fmt.Println(orange)
    // Output: 5
    

    That is a subtle one, but the fix is simple. You need to redefine the String() method on a value instead of a pointer, and in that case, it will work for both pointers and values:

    func (o Orange) String() string {
    	return fmt.Sprintf("%v", o.Quantity)
    }
    View
  • 7.

    How do you compare two structs? What about two interfaces? Provide examples.

    Answer:

    You can compare two structs with the == operator, as you would do with other simple types. Just make sure they do not contain any slices, maps, or functions, in which case the code will not be compiled.

    type Foo struct {
    	A int
    	B string
    	C interface{}
    }
    a := Foo{A: 1, B: "one", C: "two"}
    b := Foo{A: 1, B: "one", C: "two"}
    println(a == b)
    // Output: true
    type Bar struct {
    	A []int
    }
    a := Bar{A: []int{1}}
    b := Bar{A: []int{1}}
    println(a == b)
    // Output: invalid operation: a == b (struct containing []int cannot be compared)
    

    You can compare two interfaces with the == operator as long as the underlying types are “simple” and identical. Otherwise the code will panic at runtime:

    var a interface{}
    var b interface{}
    a = 10
    b = 10
    println(a == b)
    // Output: true
    a = []int{1}
    b = []int{2}
    println(a == b)
    // Output: panic: runtime error: comparing uncomparable type []int
    

    Both structs and interfaces which contain maps, slices (but not functions) can be compared with the reflect.DeepEqual() function:

    var a interface{}
    var b interface{}
    a = []int{1}
    b = []int{1}
    println(reflect.DeepEqual(a, b))
    // Output: true
    a = map[string]string{"A": "B"}
    b = map[string]string{"A": "B"}
    println(reflect.DeepEqual(a, b))
    // Output: true
    temp := func() {}
    a = temp
    b = temp
    println(reflect.DeepEqual(a, b))
    // Output: false
    

    For comparing byte slices, there are nice helper functions in the bytes package: bytes.Equal()bytes.Compare(), and bytes.EqualFold(). The latter is for comparing text strings disregarding the case, which are much faster than the reflect.DeepEqual().

    View
  • 8.

    How do you copy a slice, a map, and an interface?

    Answer:

    You copy a slice by using the built-in copy() function:

    a := []int{1, 2}
    b := []int{3, 4}
    check := a
    copy(a, b)
    fmt.Println(a, b, check)
    // Output: [3 4] [3 4] [3 4]
    

    Here, the check variable is used to hold a reference to the original slice description to make sure it is really copied.

    In the next example, on the other hand, operation does not copy the slice contents, only the slice description:

    a := []int{1, 2}
    b := []int{3, 4}
    check := a
    a = b
    fmt.Println(a, b, check)
    // Output: [3 4] [3 4] [1 2]
    

    You copy a map by traversing its keys. Yes, unfortunately, this is the simplest way to copy a map in Go:

    a := map[string]bool{"A": true, "B": true}
    b := make(map[string]bool)
    for key, value := range a {
    	b[key] = value
    }
    

    Following example copies just the description of the map:

    a := map[string]bool{"A": true, "B": true}
    b := map[string]bool{"C": true, "D": true}
    check := a
    a = b
    fmt.Println(a, b, check)
    // Output: map[C:true D:true] map[C:true D:true] map[A:true B:true]
    

    There’s no built-in way in Go to copy an interface. No, the reflect.DeepCopy() function does not exist.

    View
  • 9.

    How do you swap two values? Provide a few examples.

    Answer:

    Two values are swapped as easy as this:

    a, b = b, a
    

    To swap three values, we would write:

    a, b, c = b, c, a
    

    The swap operation in Go is guaranteed from side effects. The values to be assigned are guaranteed to be stored in temporary variables before starting the actual assigning, so the order of assignment does not matter. The result of the following operation: a := 1; b := 2; a, b, a = b, a, b is still a = 2 and b = 1, without the risk of changing the value a to the new re-assigned value. This is useful to rely on in many algorithm implementations.

    For example, a function that reverses a slice of integers in place:

    func reverse(s []int) {
            for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
                    s[i], s[j] = s[j], s[i]
            }
    }
    func main() {
    	a := []int{1, 2, 3}
    	reverse(a)
    	fmt.Println(a)
    	// Output: [3 2 1]
    }
    View
  • 10.

    Why would you prefer to use an empty struct{}? Provide some examples of the good use of the empty struct{}.

    Answer:

    You would use an empty struct when you would want to save some memory. Empty structs do not take any memory for its value.

    a := struct{}{}
    println(unsafe.Sizeof(a))
    // Output: 0
    

    This saving is usually insignificant and is dependent on the size of the slice or a map. Although, more important use of an empty struct is to show a reader you do not need a value at all. Its purpose in most cases is mainly informational. Here are a few examples where it can be useful:

    • When implementing a data set:
    set := make(map[string]struct{})
    for _, value := range []string{"apple", "orange", "apple"} {
       set[value] = struct{}{}
    }
    fmt.Println(set)
    // Output: map[orange:{} apple:{}]
    
    • With the seen hash, like when traversing a graph:
    seen := make(map[string]struct{})
    for _, ok := seen[v]; ok {
        // First time visiting a vertex.
        seen[v] = struct{}{}
    }
    
    • When building an object, and only being interested in a grouping of methods and no intermediary data, or when you do not plan to retain the object state. In the example below it does not make a difference whether the method is called on the same (case #1) or on two different objects (case #2):
    type Lamp struct{}
    func (l Lamp) On() {
            println("On")
    }
    func (l Lamp) Off() {
            println("Off")
    }
    func main() {
           	// Case #1.
           	var lamp Lamp
           	lamp.On()
           	lamp.Off()
           	// Output:
           	// on
           	// off
           	// Case #2.
           	Lamp{}.On()
           	Lamp{}.Off()
           	// Output: 
           	// on
           	// off
    }
    
    • When you need a channel to signal an event, but do not really need to send any data. This event is also not the last one in the sequence, in which case you would use the close(ch) built-in function.
    func worker(ch chan struct{}) {
    	// Receive a message from the main program.
    	<-ch
    	println("roger")
    	// Send a message to the main program.
    	close(ch)
    }
    func main() {
    	ch := make(chan struct{})
    	go worker(ch)
    	// Send a message to a worker.
    	ch <- struct{}{}
    	// Receive a message from the worker.
    	<-ch
    	println(“roger")
    	// Output:
    	// roger
    	// roger
    }
    View
  • 11.

    Explain how you can write multiline strings in GO?

    Answer:

    To write multiline string in GO you can use a raw string literal, where the string is delimited by back quotes rather than double quotes.

    ‘ line  1

    line  2

    line  3 ’

    View
  • 12.

    In GO language how you can check variable type at runtime?

    Answer:

    A special type of switch is dedicated in GO to check variable type at runtime, this switch is referred as type switch.  Also, you can switch on the type of an interface value with Type Switch.

    View
  • 13.

    Explain what Type assertion is used for and how it does it?

    Answer:

    Type conversion is used to convert dissimilar types in GO.  A type  assertion takes an interface value and retrieve from it a value of the specified explicit type.

    View
  • 14.

    Explain GO Interfaces ?

    Answer:

    In GO, interfaces is a way to specify the behaviour of an object.  An interface is created by using the “type” word, followed by a name and the keyword interface.  An interface is specified as two things.

    • A set of methods
    • Also it is referred as type
    View
  • 15.

    Explain how arrays in GO works differently then C ?

    Answer:

    In GO Array works differently than it works in C

    • Arrays are values, assigning one array to another copies all the elements
    • If you pass an array to a function, it will receive a copy of the array, not a pointer to it
    • The size of an array is part of its type. The types [10] int and [20] int are distinct
    View
  • 16.

    How you can format a string without printing?

    Answer:

    To format a string without printing you have to use command

    return fmt.Sprintf ( “at %v, %s” , e.When , e.What )

    View
  • 17.

    Explain how pointer is represented in GO?

    Answer:

    In GO a pointer is represented by using the * (asterisk) character followed by the type of the stored value.

    View
  • 18.

    Explain how you to access command line arguments passed to a GO program?

    Answer:

    You can access the command line argument using the os.Args variables. For example,

    Package main
    import (
    	“fmt”
    	“OS”
    	)
    func main () {
    	fmt.Println(len(os.Args), os.Args)
    }

     

    View
  • 19.

    Explain what is go routine in GO? How you can stop go routine?

    Answer:

    A goroutine is a function which is capable of running concurrently with other functions

    To stop goroutine, you pass the goroutine  a signal channel, that signal channel is used to push a value into when you want the goroutine to stop.  The goroutine polls that channel regularly as soon as it detects a signal, it quits.

    Quit : = make (chan bool)
    go func ( ) {
    	for  {
    		select {
    			case <- quit:
    			return
    			default
    // do other stuff
    		}
    	}
    }()
    // Do stuff
    // Quit goroutine
    Quit <- true

     

    View
  • 20.

    List out the built in support in GO?

    Answer:

    The available built-in-support in GO includes

    • Container: container/list , container/heap
    • Web Server: net/http
    • Cryptography: Crypto/md5 , crypto/sha1
    • Compression: compress/ gzip
    • Database: database/sql
    View

© 2017 QuizBucket.org