Golang or most popularly known as Go is one of the youngest programming languages that was released in the year 2012 at Google by developers Robert Griesemer, Rob Pike and Ken Thompson. It is said that Golang was born out of frustration with the demerits of the existing programming languages. Go is a high level, an open-source programming language that was mainly developed keeping in mind the efficiency of code without compromising on the simplicity and faster compilation time to help develop software applications at a faster pace. Companies like Google, Apple, Uber are using Golang due to its proven ability of less learning time, faster code development, improved runtime efficiency, reduced bugs, concurrency, garbage collection strategies and so on. In this article, we will see the most commonly asked interview questions for both freshers and experienced in Golang.
Go is a high level, a general-purpose programming language that is very strongly and statically typed by providing support for garbage collection and concurrent programming. In Go, the programs are built by using packages that help in managing the dependencies efficiently. It also uses a compile-link model for generating executable binaries from the source code. Go is a simple language with elegant and easy to understand syntax structures. Go has a builtin collection of powerful standard libraries that helps developers in solving problems without the need for third party packages. Go has first class support for Concurrency having ability to use multi-core processor architectures to the advantages of the developer and utilize memory efficiently. This helps the applications scale in a simpler way.
Go language follows the principle of maximum effect with minimum efforts. Every feature and syntax of Go was developed to ease the life of programmers. Following are the advantages of Go Language:
Go Packages (in short pkg
) are nothing but directories in the Go workspace that contains Go source files or other Go packages themselves. Every single piece of code starting from variables to functions are written in the source files are in turn stored in a linked package. Every source file should belong to a package.
From the image below, we can see that a Go Package is represented as a box where we can store multiple Go source files of the .go extension. We can also store Go packages as well within a package.
The package is declared at the top of the Go source file as
package <package_name>
The packages can be imported to our source file by writing:
import <package_name>
An example of the Go package is fmt
. This is a standard Go Package that has formatting and printing functionalities such as Println()
.
Go is a case-sensitive language.
Go Pointers are those variables that hold the address of any variables. Due to this, they are called special variables. Pointers support two operators:
*
operator: This operator is called a dereferencing operator and is used for accessing the value in the address stored by the pointer.&
operator: This operator is called the address operator and is used for returning the address of the variable stored in the pointer.This is illustrated in the diagram below. Here, consider we have a variable x assigned to 100. We store x in the memory address 0x0201. Now, when we create a pointer of the name Y for the variable x, we assign the value as &x
for storing the address of variable x. The pointer variable is stored in address 0x0208. Now to get the value stored in the address that is stored in the pointer, we can just write int z:= *Y
Pointers are used for the following purposes:
String literals are those variables storing string constants that can be a single character or that can be obtained as a result of the concatenation of a sequence of characters. Go provides two types of string literals. They are:
for
loop in Golang? Explain.Go language follows the below syntax for implementing for loop.
The for loop works as follows:
init
steps gets executed first. This is executed only once at the beginning of the loop. This is done for declaring and initializing the loop control variables. This field is optional as long as we have initialized the loop control variables before. If we are not doing anything here, the semicolon needs to be present.condition
is then evaluated. If the condition
is satisfied, the loop body is executed.
increment
statement which updated the loop control variables. The condition is evaluated again and the process repeats until the condition becomes false.Range
is mentioned, then the loop is executed for each item in that Range.Consider an example for for
loop. The following code prints numbers from 1 to 5.
Output of this code is:
The variable scope is defined as the part of the program where the variable can be accessed. Every variable is statically scoped (meaning a variable scope can be identified at compile time) in Go which means that the scope is declared at the time of compilation itself. There are two scopes in Go, they are:
A goroutine is nothing but a function in Golang that usually runs concurrently or parallelly with other functions. They can be imagined as a lightweight thread that has independent execution and can run concurrently with other routines. Goroutines are entirely managed by Go Runtime. Goroutines help Golang achieve concurrency.
go
keyword before the method call. The method will now be called and run as a goroutine. Consider an example below:In this code, we see that the sampleRoutine() function is called by specifying the keyword go before it. When a function is called a goroutine, the call will be returned immediately to the next line of the program statement which is why "Started Main" would be printed first and the goroutine will be scheduled and run concurrently in the background. The sleep statement ensures that the goroutine is scheduled before the completion of the main goroutine. The output of this code would be:
Yes. Multiple values can be returned in Golang by sending comma separated values with the return statement and by assigning it to multiple variables in a single statement as shown in the example below:
In the above example, we have a function reverseValues which simply returns the inputs in reverse order. In the main goroutine, we call the reverseValues function and the values are assigned to values val1 and val2 in one statement. The output of the code would be
Yes, this can be achieved by writing as shown below:
Here, we are assigning values of a type Integer number, Floating-Point number and string to the three variables in a single line of code.
Slice in Go is a lightweight data structure of variable length sequence for storing homogeneous data. It is more convenient, powerful and flexible than an array in Go. Slice has 3 components:
For example: Consider an array of name arr having the values "This","is", "a","Go","interview","question".
Here, we are trying to slice the array to get only the first 3 words starting from the word at the first index from the original array. Then we are finding the length of the slice and the capacity of the slice. The output of the above code would be:
The same is illustrated in the diagram below:
Go interfaces are those that have defined set of method signatures. It is a custom type who can take values that has these methods implementation. The interfaces are abstract which is why we cannot create its instance. But we can create a variable of type interface and that variable can then be assigned to a concrete value that has methods required by the interface. Due to these reasons, an interface can act as two things:
They are created by using the type
keyword followed by the name needed for the interface and finally followed by the keyword interface
. The syntax goes as follows:
Consider an example of creating an interface of the name "golangInterfaceDemo" having two methods demo_func1() and demo_func2(). The interface will be defined as:
Interface also promotes abstraction. In Golang, we can use interfaces for creating common abstractions which can be used by multiple types by defining method declarations that are compatible with the interface. Conside the following example:
In the above example, we have created 3 types for the shapes triangle, square and rectangle. We have also defined 3 Area() functions that calculate the area of the shapes based on the input object type passed. We have also defined an interface named Area and we have defined the method signature Area() within it. In the main function, we are creating the objects, assigning each object to the interface and calculating the area by calling the method declared in the interface. Here, we need not know specifically about the function that needs to be called. The interface method will take care of this considering the object type. This is called abstraction. The output of the above code will be:
Golang is faster than other programming languages because of its simple and efficient memory management and concurrency model. The compilation process to machine code is very fast and efficient. Additionally, the dependencies are linked to a single binary file thereby putting off dependencies on servers.
A map, in general, is a collection of elements grouped in key-value pairs. One key refers to one value. Maps provide faster access in terms of O(1) complexity to the values if the key is known. A map is visualized as shown in the image below:
Once the values are stored in key-value pairs in the map, we can retrieve the object by using the key as map_name[key_name]
and we can check if the key, say "foo", is present or not and then perform some operations by using the below code:
From the above code, we can see that two variables are being initialized. The val variable would get the value corresponding to the key "foo" from the map. If no value is present, we get "zero value" and the other variable isExists will get a bool value that will be set to true if the key "foo" is present in the map else false. Then the isExists condition is evaluated, if the value is true, then the body of the if would be executed.
Go channel is a medium using which goroutines communicate data values with each other. It is a technique that allows data transfer to other goroutines. A channel can transfer data of the same type. The data transfer in the channel is bidirectional meaning the goroutines can use the same channel for sending or receiving the data as shown in the image below:
A channel can be created by adding the chan
keyword as shown in the syntax below:
It can also be created by using the make() function as:
To send the data to a channel, we can use the <-
operator as shown in the syntax:
To receive data sent by the send operator, we can use the below syntax:
demo_func()
as shown in the below code?A. Since the function has return type of the struct, the function returns a copy of the struct by setting value as 1.
B. Since the function returns *DemoStruct
, which is a pointer to the struct, it returns a pointer to the struct value created within the function.
C. Since the function expects the existing struct object as a parameter and in the function we are setting the value of its attribute, at the end of execution the value of Val variable of the struct object is set to 1.
Yes, we can do that by using the Sprintf command as shown in the example below:
The fmt.Sprintf
function formats a string and returns the string without printing it.
The type assertion takes the interface value and retrieves the value of the specified explicit data type. The syntax of Type Assertion is:
Here, the statement asserts that the interface value i has the concrete type T and assigns the value of type T to the variable t. In case i does not have concrete type T, then the statement will result in panic.
For testing, if an interface has the concrete type, we can do it by making use of two values returned by type assertion. One value is the underlying value and the other is a bool value that tells if the assertion is completed or not. The syntax would be:
Here, if the interface value i have T, then the underlying value will be assigned to t and the value of isSuccess becomes true. Else, the isSuccess statement would be false and the value of t would have the zero value corresponding to type T. This ensures there is no panic if the assertion fails.
In Go, we can use a special type of switch for checking the variable type at runtime. This switch statement is called a "type switch".
Consider the following piece of code where we are checking for the type of variable v and performing some set of operations.
In the above code, we are checking for the type of variable v, if the type of variable is uint64, then the code prints "Integer type". If the type of variable is string, the code prints "String type". If the type doesnt match, the default block is executed and it runs the statements in the default block.
Using global variables in goroutines is not recommended because it can be accessed and modified by multiple goroutines concurrently. This can lead to unexpected and arbitrary results.
Empty struct is used when we want to save memories. This is because they do not consume any memory for the values. The syntax for an empty struct is:
The size of empty struct would return 0 when using println(unsafe.Sizeof(a))
The important use of empty struct is to show the developer that we do not have any value. The purpose is purely informational. Some of the examples where the empty struct is useful are as follows:
To copy a slice: We can use the built-in method called copy()
as shown below:
In the above example, we are copying the value of slice2
into slice1
and we are using the variable slice3
for holding a reference to the original slice to check if the slice has been copied or not. The output of the above code would be:
If we want to copy the slice description alone and not the contents, then we can do it by using the = operator as shown in the code below:
The output of the code will be:
Here, we can see that the contents of slice3 are not changed due to the = operator.
To copy a map in Go: We can copy a map by traversing the keys of the map. There is no built-in method to copy the map. The code for achieving this will be:
From this code, we are iterating the contents of map1 and then adding the values to map2 to the corresponding key.
If we want to copy just the description and not the content of the map, we can again use the = operator as shown below:
The output of the below code would be:
The GoPATH variable is an environment variable that is used for symbolizing the directories out of $GoROOT
which combines the source and the binaries of Go Projects. The GoROOT variable determines where the Go SDK is located. We do not have to modify the variable unless we plan to use multiple Go versions. The GoPATH determines the root of the workspace whereas the GoROOT determines the location of Go SDK.
In Go, the errors are nothing but an interface type where any type implementing the single Error() method is considered as an error. Go does not have try/catch methods as in other programming languages for handling the errors. They are instead returned as normal values. Following is the syntax for creating the error interface:
We use this whenever we apprehend that there are possibilities where a function can go wrong during type conversions or network calls. The function should return an error as its return variable if things go wrong. The caller has to check this error value and identify the error. Any value other than nil is termed as an error.
As part of good error handling practices, guard classes should be used over if-else statements. They should also be wrapped in a meaningful way as they can be passed up in the call stack. Errors of the same types should not be logged or handled multiple times.
Channels are safe for concurrent access because they have blocking/locking mechanisms that does not let goroutines share memory in the presence of multiple threads.
Maps are unsafe because they do not have locking mechanisms. While using maps, we have to use explicit locking mechanisms like mutex for safely sending data through goroutines.
We can sort slices of custom structs by using sort.Sort and sort.Stable functions. These methods sort any collection that implements sort.Interface interface that has Len(), Less() and Swap() methods as shown in the code below:
Consider an example of a Human
Struct having name and age attributes.
Also, consider we have a slice of struct Human of type AgeFactor that needs to be sorted based on age. The AgeFactor implements the methods of the sort.Interface. Then we can call sort.Sort() method on the audience as shown in the below code:
This code would output:
Shadowing is a principle when a variable overrides a variable in a more specific scope. This means that when a variable is declared in an inner scope having the same data type and name in the outer scope, the variable is said to be shadowed. The outer variable is declared before the shadowed variable.
Consider a code snippet as shown below:
Here, we have a function called countRedCars where we will be counting the red cars. We have the numOfCars variable defined at the beginning indicated by the Line 1
comment. Inside the countRedCars method, we have an if statement that checks whether the colour is red and if red then increments the numOfCars by 1. The interesting point here is that the value of the numCars variable after the end of the if statement will not be affecting the value of the numOfCars variable in the outer scope.
The function that takes a variable number of arguments is called a variadic function. We can pass zero or more parameters in the variadic function. The best example of a variadic function is fmt.Printf
which requires one fixed argument as the first parameter and it can accept any arguments.
...
) which indicates that the function can take any number of parameters if the type is specified.... type
can be visualised as a slice. We can also pass the existing slice (or multiple slices) of the mentioned type to the function as a second parameter. When no values are passed in variadic function, the slice is treated as nil.Consider an example code below:
Here, we have a variadic function called joinstring that takes a variable number of arguments of a type string. We are trying to join the arguments separated by the hyphen symbol. We are demonstrating the variadic function behaviour by first passing 0 arguments and then passing multiple arguments to the function. The output of this code is:
byte
and rune
are two integer types that are aliases for uint8
and int32
types respectively.
The byte represents ASCII characters whereas the rune represents a single Unicode character which is UTF-8 encoded by default.
'a'
,'b'
,'\n'
.0x61
in hexadecimal corresponds to the rune literal a
.Consider we have num1=2, num2=3. To swap these two numbers, we can just write:
num1,num2 = num2, num1
The same logic can be extended to list of variables as shown below:
The code results in the output:
Factorial of a number is the product of multiplication of a number n with every preceeding number till it reaches 1. Factorial of 0 is 1.
Code:
The output of this code would be:
To find the nth fibonacci number, we have to add the previous 2 fibonacci numbers as shown below.
Code:
The output of this code would be:
We can do this by using the Contains() method from the strings package.
The output of this code is:
We can do this by using the Compare() method from the bytes package.
The output of this code is:
Golang was developed with the promise of code efficiency for faster software development. Companies have recognized the scope and benefits of Golang and have started to adapt to this language. Some of the notable companies that have already shifted to Golang are Google, Apple, Facebook, Docker, BBC etc. Furthermore, Golang has raised the excitement level of developers in the open-source community as it's been a while since a new language for the backend has been created. Due to these reasons, the scope of Golang is growing rapidly. According to the data in Golang Cafe from 2021, the average salary of golang developers in India starts from ₹819,565 to ₹1,617,391 per annum. The prospects and benefits are amazing! In this article, we have seen the most commonly asked golang interview questions for both freshers and experienced professionals.
To Learn Golang:
https://golang.org/
https://gobyexample.com/
Golang Playground to try out code: https://play.golang.org/
a
for the below code?fmt.Printf("Type of a is %T\n", a)
fmt.Printf("Type of a is %D\n", a)
fmt.Printf("Type of a is %X\n", a)
fmt.Printf("Type of a is %DT\n", a)
slice1
of the below code?[2 3 5 7]
[3 5 7]
[2 3 5]
[3 5 7 11]
fmt.Println(runtime.CPU())
fmt.Println(runtime.CPUNum())
fmt.Println(runtime.NumCPU())
fmt.Println(runtime.CPUCount())
0,[3,5,2]
3,[3,5,2]
3,[5,2]