Golang also known as Go is an open-source, compiled and statically typed programming language developed by Google. Go is a compiled, concurrent, statically-typed general purpose programming language with simple syntax and a robust standard libraries. Go improves upon concepts like imperative and object-oriented programming and thereby simplifies the working environment for developers. In this guide, you will learn everything you need to know to get started using Go to build real-world applications. Why learn Golang? Now you still might be asking why should I learn Golang with all the other alternatives out there. Here is a list of some of the pros of the Go programming language. Simple syntax - Go has concise and straightforward syntax that makes writing readable and maintainable code easy. Compiled language Static linking - The compiler supports static linking which means you can statically link your project into one massive binary and then simply deploy it to the cloud or a server. Open source - Go is open-source so you can read the source code and contribute to the repository. Now that you have a sense of what Golang is and what it brings to the table let's jump into the installation and basics. Installation Go can be installed on all three major platforms Windows, Linux and Mac. Windows: Download the latest and execute it on your machine. Follow the instruction prompts to install Go in your root directory and automatically set up an environment variable for your terminal. Windows Golang MSI Linux: On Linux you can download the tar file from the and unzip it to /usr/local. official website Mac: On Mac you can download the Mac installer from the and execute it using a double tap. official website Verifying the installation: You can verify the installation by running the following command in your terminal. go --version After installing, you will continue by taking a look at the basic syntax and features Golang provides. Basic structure Let's start by taking a look at the basic structure of a Golang program. For that, we are going to take a look at a hello world example. package main import "fmt" func main() { fmt.Println("Hello World!") } All Go programs must be part of a package, and here you use main to make the file executable. The main package requires a main function, which will be called when running the file. You must import all the packages you use including all the standard libraries like fmt in this case. Use the following command in your terminal to run the file: go run main.go Variables In this section, you will learn the different ways to declare and initialize variables. Golang is statically typed, which means that variables either explicitly or implicitly assign a type before your program runs. Declaring variables: Variables can be declared using the keyword followed by the and the . var variable name datatype var i int var s string Initializing variables: After declaring the variables they can be initialized using the operator. = i = 20 s = "Some String" It is equally valid to declare and initialize the variable in a single line. var k int = 35 You can also omit the type when initializing the variable at the time of declaration. var score = 10 Short variable declaration: The keyword can also be omitted using the short variable declarator. var := j := 50 str := "Some String!" Declaring multiple variables: It is also possible to declare multiple variables in a single line of code. firstName, lastName := "FirstName", "LastName" Variable Declaration Block: Variable declarations can also be grouped together for better readability and cleaner code. var ( name = "Donald Duck" age = 50 ) Constants A constant is a variable with a fixed value that cannot change under any circumstances. Declaring a constant: Constants are declared by using the keyword instead of . You must assign the value at the time of the declaration because the value will fix it after that. const var const PI float64 = 3.14159265359 const VALUE = 1000 Note: The names of constant variables are usually written in uppercase. Declaring using a declaration block: Constants can also be declared using declaration blocks for better readability. const ( PRODUCT = "Ice Cream" QUANTITY = 50 ) Datatypes As already mentioned Golang is a statically typed programming language, which means that variables always have a type that cannot change. Here is a list of all the datatypes available in Golang: uint8 unsigned 8-bit integers (0 to 255) uint16 unsigned 16-bit integers (0 to 65535) uint32 unsigned 32-bit integers (0 to 4294967295) uint64 unsigned 64-bit integers (0 to 18446744073709551615) int8 signed 8-bit integers (-128 to 127) int16 signed 16-bit integers (-32768 to 32767) int32 signed 32-bit integers (-2147483648 to 2147483647) int64 signed 64-bit integers (-9223372036854775808 to 9223372036854775807) float32 IEEE-754 32-bit floating-point numbers float64 IEEE-754 64-bit floating-point numbers complex64 complex numbers with float32 real and imaginary parts complex128 complex numbers with float64 real and imaginary parts byte alias for uint8 rune alias for int32 uint unsigned, either 32 or 64 bits int signed, either 32 or 64 bits uintptr unsigned integer large enough to store the uninterpreted bits of a pointer value You can set the data type of a variable after the variable name. // Integer var i int = 5 fmt.Println(i) // String var s string = "Hello World!" fmt.Println(s) // Float var f float64 = 3.14159265359 fmt.Println(f) // Boolean var b bool = true fmt.Println(b) Converting Datatypes Converting also known as casting datatypes, is an important concept when needing a precise type is expected. This section will show you the most common type conversions. String to float: One of the most common conversions is a string to a float. For this, you use the strconv package, which is the standard package for converting strings to basic datatypes. s := "3.1415926535" f, err := strconv.ParseFloat(s, 8) if err != nil { fmt.Println("Error convert string to integer", err) } fmt.Println(reflect.TypeOf(f)) Float to string: Converting a float to a string is a bit easier because their cannot be an error. var flo float64 = 3.1415926535 var strFlo string = strconv.FormatFloat(flo, 'E', -1, 32) fmt.Println(reflect.TypeOf(strFlo)) Float to int: Casting of basic datatypes like int and floats is a lot easier and can be done using the following syntax. var f32 float32 = 3.1415926535 fmt.Println(reflect.TypeOf(f32)) i32 := int32(f32) fmt.Println(reflect.TypeOf(i32)) Operators An operator is a symbol that tells the Go compiler to perform a specific action. The following code blocks will show you all the basic operators of the Go programming language and how you can use them in your applications. Arithmetic Operators: Arithmetic operators are used to perform common arithmetic actions like adding or subtracting numbers. Here is a list of the most common arithmetic operators: '+' (Addition) - Adds to operands '-' (Subtraction) - Builds the sum of two numbers by subtracting them '*' (Multiplication) - Multiplies two values with each other '/' (Division) - Divides two values '%' (Modulus) - Gets the remainder after an integer division Let's look at how you can actually use these operators in your applications. package main import "fmt" func main() { var i int = 10 var k int = 20 // Arithmetic Operators fmt.Printf("i + k = %d\n", i+k) fmt.Printf("i - k = %d\n", i-k) fmt.Printf("i * k = %d\n", i*k) fmt.Printf("i / k = %d\n", i/k) fmt.Printf("i mod k = %d\n", i%k) } The output of running the above program should be: # Output i + k = 30 i - k = -10 i * k = 200 i / k = 0 i mod k = 10 Comparison Operators: Comparison operators are used to compare two values with each other. '==' (Equal to) - Returns true if x is equal to y '!=' (Not equal to) - Returns true if x is not equal y '<' (Lesser than) - Returns true if x is lesser than y '<=' (Lesser than Equal to) - Returns true if x is lesser than or equal to y '>' (Greater than) - Returns true if x is greater than y '>=' (Greater than Equal to) - Returns true if x is greater than or equal to y Here is an example of these operators in action. package main import "fmt" func main() { var i int = 10 var k int = 20 // Comparison Operators fmt.Println(i == k) fmt.Println(i != k) fmt.Println(i < k) fmt.Println(i <= k) fmt.Println(i > k) fmt.Println(i >= k) } You should see the following output when running the program: # Output false true true true false false Logical Operators: Logical operators are used to combine multiple conditions or to complement the evaluation of the original condition. '&&' (Logical AND) - Returns true if all conditions are true '||' (Logical OR) - Returns true if at least one condition is true '!' (Logical NOT) - Inverts the result of the condition e.g. true to false and vise versa Here is an example to show you the operators in action: package main import "fmt" func main() { var i int = 10 var k int = 20 var z int = 30 // Logical Operators fmt.Println(i < z && i > k) fmt.Println(i < z || i > k) fmt.Println(!(i == z && i > k)) } You should see the following output when running the program: # Output false true true Assignment Operators: Assignment operators are used to assign a specific value to a variable. '=' (Simple Assignment) - Assigns the value to the variable '+=' (Add Assignment) - Adds the value to the current value of the variable '-=' (Subtract Assignment) - Subtracts the value to the current value of the variable '*=' (Multiply Assignment) - Multiplies the current value of the variable with the new value '/=' (Division Assignment) - Divides the current value of the variable by the new value '%=' (Modulus Assignment) - Takes modulus of the two values and assigns the result to left operand Here is a practical example of the assignment operators: package main import "fmt" func main() { // Assignment Operators var x, y = 15, 25 x = y fmt.Println("= ", x) x = 15 x += y fmt.Println("+=", x) x = 50 x -= y fmt.Println("-=", x) x = 2 x *= y fmt.Println("*=", x) x = 100 x /= y fmt.Println("/=", x) x = 40 x %= y fmt.Println("%=", x) } You should see the following output when running the program: # Output = 25 += 40 -= 25 *= 50 /= 4 %= 15 Functions Functions are code blocks that help you divide your program into smaller packs of code that are reusable and easily readable. In this section, you will learn everything you need to know to start writing functions in Golang including declaration, parameters, return values and many more. Declaration: Functions can be declared using the keyword followed by the function name, a pair of parentheses and a code block containing the functionality. func The function can then be called using the function name followed by parentheses. Here is an example of a basic hello world function that takes no parameters and returns no value. package main import "fmt" func main() { helloWorld() } func helloWorld() { fmt.Println("Hello World!") } Parameters: Sometimes you will need to pass information to your function, which can be done using parameters. Arguments are specified after the first pair of parentheses and are basically just variables. package main import "fmt" func main() { hello("Go") add(20, 30) } func hello(x string) { fmt.Printf("Hello %s\n", x) } func add(x int, y int) { fmt.Println(x + y) } You should see the following output when running the program: Hello Go 50 Note: You can pass as many arguments as you want. But it is good practice to move everything higher than 4 or 5 into some kind of object or struct for better readability. Return values: Returning values from a function is another essential concept and can be done by providing a return type after the first pair of parentheses. You can then use the return statement, as shown in the following example. package main import "fmt" // Returning a single value of type int func add(x int, y int) int { return x + y } func main() { // Accepting return value in variable sum := add(20, 30) fmt.Println("Sum: ", sum) } Named return value: You can also define a function with a named return type that has the advantage of not providing the variable name in the return statement. package main import "fmt" // Named return value func getArea(l int, b int) (area int) { area = l * b return // Return without specify variable name } func main() { // Accepting a named return value area := getArea(10, 10) fmt.Println("Area: ", area) } Return multiple values: Golang also allows you to return multiple parameters, which can help in numerous practical scenarios like error handling while returning a second variable. package main import "fmt" // Returning multiple name values func rectangle(l int, b int) (area int, parameter int) { parameter = 2 * (l + b) area = l * b return } func main() { // Accepting multiple return values area, parameter := rectangle(10, 10) fmt.Println("Area: ", area) fmt.Println("Parameter", parameter) } Passing addresses to function: It is also possible to pass the address of a variable to a function and then modify them using the reference inside your function. package main import "fmt" // Passing addresses to a function func addValue(x *int, y *string) { *x = *x + 5 *y = *y + " World!" return } func main() { var number = 20 var text = "Hello" fmt.Println("Before:", text, number) addValue(&number, &text) fmt.Println("After:", text, number) } Anonymous functions: An anonymous function is a function that doesn't contain any name, which can be useful if you want to create an inline function. package main import "fmt" func main() { func(name string) { fmt.Println("Hello ", name) }("Everyone!") } Assigning function to a variable: Anonymous functions allow you to assign a function to a variable, as shown in the example below. package main import "fmt" // Defining a anonymous function var ( area = func(l int, b int) int { return l * b } ) func main() { area := area(10, 10) fmt.Println(area) } Closure functions: Closure functions are a special case of an anonymous function where you access outside variables. package main import "fmt" func main() { l := 10 b := 10 // Closure functions are a special case of a anonymous function where you access outside variables func() { var area int area = l * b fmt.Println(area) }() } Variadic functions: A variadic function is a special kind of function that can take an arbitrary number of arguments. package main import ( "fmt" "reflect" ) func main() { printMultipleStrings("Hello", "World", "!") } // Passing multiple atributes using a variadic function func printMultipleStrings(s ...string) { for i := 0; i < len(s); i++ { fmt.Println(s[i]) } } Variadic functions also allow you to pass in multiple different variable types using an empty interface. package main import ( "fmt" "reflect" ) func main() { printMultipleVariables(1, "green", false, 1.314, []string{"foo", "bar", "baz"}) } // Pass multiple different datatypes func printMultipleVariables(i ...interface{}) { for _, v := range i { fmt.Println(v, "--", reflect.ValueOf(v).Kind()) } } Deferred function calls: Defer is a unique Go statement that schedules a function call to be run after the function completes. Consider the following example: package main import "fmt" func first() { fmt.Println("First Function") } func second() { fmt.Println("Second Function") } // Defer is a spezial statement that schedules functions to be executed after the function completes func main() { // The Second function will be called after the first defer second() first() } After running the code you should see that the first method got executed before the second even though the second was called first. # Output First Function Second Function Higher-order functions: Higher-order functions are functions that either receive a function as an argument or return a function as its return value. Consider the following example: package main import "fmt" func multiply(x, y int) int { return x * y } // Function that returns another function func partialMultiplication(x int) func(int) int { return func(y int) int { return multiply(x, y) } } func main() { multiple := partialMultiplication(10) fmt.Println(multiple(10)) } Control Structures Control structures are used to specify whether a block of code should be executed or not. In this section, you will learn about control structures and how you can use them in your applications. If-Else: If statements are used only to execute the code if the specified condition evaluates to true. package main import ( "fmt" ) func main() { // If Statement x := true if x == true { fmt.Println("True") } } The if statement can be followed up by an else statement, which will be executed when the if statement evaluates to false. package main import ( "fmt" ) func main() { // If-Else Statement y := 100 if y > 80 { fmt.Println("Greater than 80") } else { fmt.Println("Lesser than 80") } } If statements can also be chained using else if statements as shown in the following example. package main import ( "fmt" ) func main() { // If-Elseif Statement grade := 5 if grade == 1 { fmt.Println("You have an A") } else if grade > 1 && grade < 5 { fmt.Println("You have no A but you are positiv") } else { fmt.Println("Your grade is negativ") } } The if statement in Golang can also contain a short variable declaration preceding the conditional expression. package main import ( "fmt" ) func main() { // If statement initialization if a := 10; a == 10 { fmt.Println(a) } } Switch: A switch statement takes a specified expression and compares it against a list of multiple cases. Once the case matches the expression, it will execute the code block. package main import ( "fmt" ) func main() { // Switch Statement num := 1 switch num { case 1: fmt.Println("One") case 2: fmt.Println("Two") default: fmt.Println("Many") } } Multiple cases can also execute a single code block, as shown in the following code block. package main import ( "fmt" ) func main() { // Switch Statement with multiple cases switch num { case 1, 2, 3, 4, 5: fmt.Println("Some") case 6, 7, 8, 9: fmt.Println("More") default: fmt.Println("Many") } } Fallthrough keyword: The keyword can be used to force the execution flow to fall through the successive case block fallthrough package main import ( "fmt" ) func main() { // Switch fallthrough case statement (forces the execution of the next statement) dayOfWeek := 3 switch dayOfWeek { case 1: fmt.Println("Go to work") fallthrough case 2: fmt.Println("Buy some bread") fallthrough case 3: fmt.Println("Visit a friend") fallthrough case 4: fmt.Println("Buy some food") fallthrough case 5: fmt.Println("See your family") default: fmt.Println("No information available for that day.") } } That means if case one is executed it will automatically also execute the following case. You should see the following output after running the program. # Output Visit a friend Buy some food See your family Switch conditional statements: Switch also allows you to use conditional statements in your cases. package main import ( "fmt" ) func main() { // Switch using conditional statements switch { case num < 5: fmt.Println("Smaller than 5") case num == 5: fmt.Println("Five") case num > 5: fmt.Println("Bigger than five") default: fmt.Println("No information about the number") } } Switch initializer: The switch statement also allows you to initialize the variable directly after the switch keyword, which will only be accessible inside the statement. package main import ( "fmt" ) func main() { // Switch initializer statement switch number := 5; { case number < 5: fmt.Println("Smaller than 5") case number == 5: fmt.Println("Five") case number > 5: fmt.Println("Bigger than five") default: fmt.Println("No information about the number") } } Loops Loops are used to iterate over sequences or execute a code block a certain amount of times. Golang contains only a single loop type, which is the for loop. The for loop also replaces the functionality of a while loop in other languages. For loop: The most basic use case of a for loop is when you already know how often you want to loop through beforehand. Here is an example of looping from 0 to 9 and printing out the output. package main import ( "fmt" ) func main() { // Basic for loop for i := 0; i <= 10; i++ { fmt.Println(i) } } You can also define an infinite loop and stop it using the break statement inside the loop, as shown in this example. package main import ( "fmt" ) func main() { // Infinite loop i := 0 for { fmt.Println("Hello World!") // Breaks/Stops the infinite loop if i == 10 { break } i++ } } The range keyword is used to iterate over expressions that evaluate to an array, slice, map or string. package main import ( "fmt" ) func main() { // Ranges strings := []string{"Hello", "World", "!"} // Get the index and value while looping through the range for i, val := range strings { fmt.Printf("%d: %s \n", i, val) } // Only getting the index for i := range strings { fmt.Println(i) } // Only getting the value for _, val := range strings { fmt.Println(val) } } While loop: As stated above, a for loop can also be used as a while loop. This loop is executed as long as the condition is true. package main import "fmt" func main() { // Basic while loop x := 0 for x < 10 { fmt.Println(x) x++ } } A do-while loop can be achieved by using a condition and the break statement inside the for loop. package main import "fmt" func main() { // Do while loop num := 0 for { // Work fmt.Println(num) if num == 10 { break } num++ } } Arrays An array is a collection of elements of a single type. It is used to hold multiple values at the same time. These values are referred to as the array elements and can be accessed using a specific index. Arrays are fixed in size and therefore cannot grow or shrink. Declaring Arrays: To declare an array you first need to specify the number of elements followed by the data type of the elements the array holds. package main import "fmt" func main() { // Declaring an Array var intArray [5]int } This example creates an integer array with a length of five. Assigning values and accessing elements: Values can be accessed and assigned by using the index number, which will be specified in square brackets. package main import "fmt" func main() { // Declaring an Array var intArray [5]int // Assigning values intArray[0] = 10 intArray[1] = 2 // Accessing the elements fmt.Println(intArray[0]) fmt.Println(intArray[1]) } Initializing an Array using Array literals: Arrays can also be initialized with predefined values using array literals. For that, you specify the number of elements, the data type of the elements followed by the predefined values wrapped in curly braces. package main import "fmt" func main() { // Initialize Array using Array literals x := [5]int{0, 5, 10, 15, 20} var y [5]int = [5]int{0, 5, 10, 15, 20} fmt.Println(x) fmt.Println(y) } You can also initialize elements for specific values using the following syntax. package main import "fmt" func main() { // Initialize values for specific array elements a := [5]int{1: 1, 4: 25} fmt.Println(a) } Initializing an Array with ellipses: The ellipse syntax can be used to automatically detect the size of the array based on the elements specified in the array declaration. package main import "fmt" func main() { // Initializing an Array with ellipses k := [...]int{10, 20, 30} fmt.Println(len(k)) } Copying an array: Copying an array can be done using two ways. You can either copy all its values or copy the reference of the array. Copy values - The copied array has the same values but is independent of the original array Copy reference - Changes to the original array will be reflected in the copied array and vise versa Here is an example for better understanding. package main import "fmt" func main() { x := [5]int{0, 5, 10, 15, 20} // Copy array values y := x // Copy by reference z := &x fmt.Printf("x: %v\n", x) fmt.Printf("y: %v\n", y) x[0] = 1 fmt.Printf("x: %v\n", x) fmt.Printf("y: %v\n", y) fmt.Printf("z: %v\n", *z) } In this example, we copy the array x into two new arrays and then change an item in the original array. The array copying the reference of the original will also change the value will the array copying the values will stay the same. Iterating over an array: Looping through array elements can be done using every kind of loop. Here are a few examples. package main import "fmt" func main() { x := [5]int{0, 5, 10, 15, 20} // Standard for loop for i := 0; i < len(x); i++ { fmt.Println(x[i]) } // Getting index and value using range for index, element := range x { fmt.Println(index, "=>", element) } // Only getting value using range for _, value := range x { fmt.Println(value) } // Range and counter j := 0 for range x { fmt.Println(x[j]) j++ } } Slices Slices are dynamic arrays that can grow and shrink as you see fit. Like arrays, slices also use indexable and have a length. Creating a slice: Slices can be created using multiple techniques: Basic slice definition by emitting the length in the square brackets Creating a slice using the build-in make() function, which takes the datatype, length and capacity as a parameter Initializing the slice using a slice literal Creating a slice using the new keyword Here is an example for all listed techniques: package main import ( "fmt" "reflect" ) func main() { // Create an empty slice var x []int fmt.Println(reflect.ValueOf(x).Kind()) // Creating a slice using the make function var y = make([]string, 10, 20) fmt.Printf("y \tLen: %v \tCap: %v\n", len(y), cap(y)) // Initialize the slice with values using a slice literal var z = []int{10, 20, 30, 40} fmt.Printf("z \tLen: %v \tCap: %v\n", len(z), cap(z)) fmt.Println(z) // Creating a Slice using the new keyword var a = new([50]int)[0:10] fmt.Printf("a \tLen: %v \tCap: %v\n", len(a), cap(a)) fmt.Println(a) } Adding items: Items can be added to a slice using the method, which takes a slice and a value as an argument. append() package main import ( "fmt" "reflect" ) func main() { // Add items using the append function var b = make([]int, 1, 10) fmt.Println(b) b = append(b, 20) fmt.Println(b) } If the space of the underlying slice is sufficient the value is saved in the slice. If not a new slice is created to store the value. Accessing items: As arrays, slices also use indices to access values. package main import ( "fmt" "reflect" ) func main() { // Access slice items var c = []int{10, 20, 30, 40} fmt.Println(c[0]) fmt.Println(c[0:3]) } Changing item values: Values can be changed by referring to a specific index and setting a new value using the operator. = package main import ( "fmt" "reflect" ) func main() { // Change item values var d = []int{10, 20, 30, 40} fmt.Println(d) d[1] = 35 fmt.Println(d) } Copying and appending items: Slices can be copied using the build-in copy() function, which copies the data of one slice to another. You can also append all items of a slice to another using the append() function and a spread operator. package main import ( "fmt" "reflect" ) func main() { // Copy slice into another slice var e = []int{10, 20, 30, 40} var f = []int{50, 60, 70, 80} copy(e, f) fmt.Println("E: ", e) // Append a slice to an existing one var g = []int{10, 20, 30, 40} var h = []int{50, 60, 70, 80} g = append(g, h...) fmt.Println(g) } Maps A map is an unordered collection that allows you to store key/value pairs comparable to a HashMap in Java or a Python dictionary. The value can then quickly be retrieved using the key, which acts as an array index. Declaration: There are multiple techniques for declaring a map. Here is a small list of the most common methods: Declaring an empty map and then adding objects Initializing the map using some default values Declaring the map using the make function Here is an example of all these techniques: package main import "fmt" func main() { // Declaring empty map var shopingList = map[string]int{} fmt.Println(shopingList) // Initializing a map var people = map[string]int{"Elon": 10, "Jeff": 15} fmt.Println(people) // Map declaration using make function var peopleList = make(map[string]int) peopleList["Elon"] = 10 peopleList["Jeff"] = 15 fmt.Println(peopleList) } Accessing and manipulating map items: Accessing and manipulating a map is quite similar to an array. Items can be accessed by providing the key in squared brackets.Adding new items is done by providing a new key to the map and assigning a value to the variable. Updating works the same, but instead of a new key you can use the already existing key you want to update.Items can be deleted using the delete function and passing the map and the key as function arguments. The function will then delete the item in the map instance itself and therefore not have a return value. package main import "fmt" var m = map[string]string{ "c": "Cyan", "y": "Yellow", "m": "Magenta", "k": "Black", } func main() { // Accessing items fmt.Println(m["c"]) // Adding items m["b"] = "black" fmt.Println(m) // Updating items m["y"] = "lemon yellow" fmt.Println(m) // Deleting items delete(m, "b") fmt.Println(m) } Iterating over a map: The for loop using the range keyword can be used to quickly iterate over a map and get both the key and value variables. Keep in mind that a map is an unordered collection and there will be no possibility to predict in which order the items will be returned. package main import "fmt" var m = map[string]string{ "c": "Cyan", "y": "Yellow", "m": "Magenta", "k": "Black", } func main() { // Iterating over a map for k, v := range m { fmt.Printf("Key: %s, Value: %s", k, v) } } Checking if an item exists: You can also check if an item exists inside the map using the second returned value when fetching an item from the map. The value is a boolean and will return false if the map does not contain the value you searched for. package main import "fmt" var m = map[string]string{ "c": "Cyan", "y": "Yellow", "m": "Magenta", "k": "Black", } func main() { // Test if an item exists c, ok := m["y"] fmt.Println("\nc: ", c) fmt.Println("ok: ", ok) } Struct Structs are a collection of data fields with predefined types and allow you to define your own data type similar to classes in Java. Each of the data fields of a struct is declared using a predefined type (Either build-in or user-defined). Structs improve the modularity of your application and help you define the structure of complex objects, which can be passed to functions or used in other ways. Declaring a struct: Declaring a struct is done by using both the type statement before the actual name and the struct statement after the name. Then you can define the fields and datatypes of every field inside the object. package main import "fmt" // Declaring a Struct type Animal struct { name string weight int } func main() { } Here we declare a struct with the name ,,Animal" which has two fields - name of type string and weight of type int. Creating a struct: After declaring a struct there are multiple ways of creating an instance. Here is a list of the most common creating processes: Declaring a variable with the struct as the datatype without initializing it Creating an instance using struct literate Creating an instance using the keyword new Using the pointer address operator to create a new instance Here are examples showcasing these methods: package main import "fmt" // Declaring a Struct type Animal struct { name string weight int } func main() { // Creating an instance of a struct var dog Animal dog.name = "Dog" dog.weight = 40 fmt.Println(dog) // Creating an instance using struct literate var cat = Animal{name: "Cat", weight: 5} fmt.Println(cat) // Creating an instance using the new keyword var bird = new(Animal) bird.name = "Bird" bird.weight = 1 fmt.Println(bird) // Creating an instance using the pointer address operator var monkey = &Animal{name: "Monkey", weight: 10} fmt.Println(monkey) } Comparing struct instances: In Go the operator or the method can be used to compare two instances of the same struct with each other. Both will first check if the two objects are an instance of the same struct and then continue by comparing the values of the fields. If all values are the same true will be returned. == reflect.DeepEqual() package main import "fmt" // Declaring a Struct type Animal struct { name string weight int } func main() { // Creating an instance var bird = new(Animal) bird.name = "Bird" bird.weight = 1 fmt.Println(bird) var monkey = &Animal{name: "Monkey", weight: 10} fmt.Println(monkey) // Comparing struct instances fmt.Println(bird == monkey) // Comparing struct instances using DeepEqual fmt.Println(reflect.DeepEqual(bird, monkey)) } Copying a struct: Copying an instance of a struct is important when you want a second instance with the same values that do not reference the original one. That means that when you are changing one of the instances the other one will not be affected. package main import "fmt" // Declaring a Struct type Animal struct { name string weight int } func main() { // Creating an instance using the pointer address operator var monkey = &Animal{name: "Monkey", weight: 10} fmt.Println(monkey) // Copying struct type using pointer reference monkey2 := monkey monkey2.name = "Monkey2" fmt.Println(monkey2) } Struct methods: Methods can be associated with a struct by defining the associated type inside of the head of the method. In the following example the function is associated with the Config struct and can be used by every instance of the Config struct. ConfInfo() package main import "fmt" type Config struct { Env string Proxy ProxyInfo } type ProxyInfo struct { Address string Port string } func (conf Config) ConfInfo() string { fmt.Println("Env: ", conf.Env) fmt.Println("Proxy: ", conf.Proxy) return "----------------------" } func main() { c := &Config{ Env: "DEBUG:TRUE", Proxy: ProxyInfo{ Address: "addr", Port: "port", }, } fmt.Println(c.ConfInfo()) } Nested struct: Structs can also be nested inside of each other by giving the field of a struct the type of another struct. This allows you to create complex data formats that can easily be reused. package main import "fmt" // Nested Struct type Configuration struct { Env string Proxy Proxy } type Proxy struct { Address string Port string } func main() { // Creating an instance of a nested struct c := &Configuration{ Env: "DEBUG:TRUE", Proxy: Proxy{ Address: "addr", Port: "port", }, } fmt.Println(c) fmt.Println(c.Proxy.Address) } Here we use the struct inside the struct as a datatype. Proxy Configuration JSON field tags: When declaring a struct you can also specify optional field tags (JSON or YAML for example). These will help you when formatting an instance of the struct into a specific format and vise versa. package main import ( "encoding/json" "fmt" ) type Dog struct { Name string `json:"name"` Weight string `json:"weight"` } func main() { json_string := ` { "name": "Rocky", "weight": "45" }` rocky := new(Dog) json.Unmarshal([]byte(json_string), rocky) fmt.Println(rocky) spot := new(Dog) spot.Name = "Spot" spot.Weight = "20" jsonStr, _ := json.Marshal(spot) fmt.Printf("%s\n", jsonStr) } The example above shows you how you can use this for converting a JSON string into an instance of the Dog struct and how you can convert an instance of the Dog struct back into JSON. Interfaces An interface is Golang is a type that defines a set of methods signatures. A type then satisfies the interface if it implements all the method signatures provided by the interface. Defining an Interface: You can define an interface using the keyword after your type name as shown below. interface package main import ( "fmt" ) // Defining a interface type User interface { PrintName(name string) } type Vehicle interface { Alert() string } func main() { } The User interface defines a single method signature called . Any type that implements the function will therefore now satisfy the User interface. PrintName() PrintName() Implementing the Interface: When implementing an interface in your struct you will not need to explicitly specify the interface that is implemented. Go will automatically determine if the struct implements the interface by checking if it implements all the method signatures defined in the interface. package main import ( "fmt" ) // Defining a interface type User interface { PrintName(name string) } type Vehicle interface { Alert() string } // Create type for interface type Usr int type Car struct{} // Implement interface function in type func (usr Usr) PrintName(name string) { fmt.Println("User Id:\t", usr) fmt.Println("User Name:\t", name) } func (c Car) Alert() string { return "Hup! Hup!" } func main() { var user1 User user1 = Usr(1) user1.PrintName("Gabriel") c := Car{} fmt.Println(c.Alert()) Print(20) } Defining an empty interface: There is also a way to define an empty interface which is often used for functions that accept any datatype. It is also often combined with the spread operator which allows for any number of parameters to be passed. ... package main import ( "fmt" ) // Define an empty interface - Often used for functions that accepts any type func Print(a ...interface{}) (n int, err error) { return fmt.Println(a...) } func main() { } The method in this example accepts any type of argument because of the empty interface and also allows the user to pass infinite parameters because of the spread operator. Print() Further steps You should now be familiar with the basics of the Go programming language, but there are still a lot of concepts to learn and you will need practice for the already learned skills. I recommend starting to practice by building real world projects. You can find a collection of all the code and a lot of real-world Go projects in the following , which should help you on the way to becoming a better Go developer. Github repository I can also recommend the , which I personally read to get up to speed on Golang (Disclaimer: Affiliate link). Manning Go in Action book Sources Here are the sources used for writing this article: Working with Go Golang Programs TutorialsPoint Golang GeekForGeek Go operators Conclusion You made it all the way until the end! I hope that this article helped you understand the basics of the Go programming language and how you can use it in your applications. If you have found this useful, please consider recommending and sharing it with other fellow developers and subscribing to my newsletter. If you have any questions or feedback, let me know using my contact form or contact me on . Twitter