Short notes about Golang
Oct 10, 2022/
#golang
/-9 minBasics
- Golang is a programming language that is similar to C but is easier to work with, compiles faster, and provides support for multithreads. It is mostly used in microservice applications.
- You can use
to see the doc for a package andgo doc fmt
to see the doc for a method.go doc fmt.Println
1func Fprint(w io.Writer, a ...any) (n int, err error)
2func Fprintf(w io.Writer, format string, a ...any) (n int, err error)
3func Fprintln(w io.Writer, a ...any) (n int, err error)
4func Print(a ...any) (n int, err error)
5func Printf(format string, a ...any) (n int, err error)
6func Println(a ...any) (n int, err error)
7func Sprint(a ...any) string
8func Sprintf(format string, a ...any) string
9func Sprintln(a ...any) string
10...
Hello World
- The structure of the simplest Golang file is as follows (use
to run the code):go run main.go
1// the organization of files is provided by packages.
2// files located in the same folder must have the same package.
3package main
4
5import "fmt"
6
7// the main function is the entry point of the program.
8// there can be a main function in each package.
9func main() {
10 fmt.Println("Hello World")
11}
Print Methods
- There are various printing functions. Those starting with
go to the console and return the number of bytes. Those withPrint
go to an external source like a file or a browser. Those withFprint
store the output in a character buffer, essentially converting it to a variable.Sprint
1package main
2
3import "fmt"
4
5func main(){
6 fmt.Println("Hello!")
7 fmt.Print("Hello!\n")
8 fmt.Printf("Hi! My name is %s. I have lived in %s for %d years. They say the weather is amazing, which is %t", "Enes", "Istanbul", 23, true)
9
10 // returns "Hello Enes!" but doesn't print to console.
11 result := fmt.Sprintf("Hello %s!", "Enes")
12}
Variables
- The data types are as follows:
- Integer: int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64
- Float: float32, float64
- String: string
- Boolean: bool
- Null: nil
- When defining variables, we can proceed in multiple ways. We can either give the variable a type or let Golang infer it.
1// first way
2count := 10
3// second way
4var age = 21
5// third way
6var minute int = 60
7// fourth way
8const MAX_COUNT = 10
9// fifth way
10var city string
11city = "Istanbul"
- We can use
to see the type of a variable.reflect.TypeOf()
1stringVar := "string"
2intVar := 10
3floatVar := 1.2
4boolVar := false
5arrayVar := []string{"foo", "bar", "baz"}
6objectVar := map[string]int{"apple": 23, "tomato": 13}
7nilVar := nil
8
9fmt.Println(reflect.TypeOf(stringVar))
10fmt.Println(reflect.TypeOf(intVar))
11fmt.Println(reflect.TypeOf(floatVar))
12fmt.Println(reflect.TypeOf(boolVar))
13fmt.Println(reflect.TypeOf(arrayVar))
14fmt.Println(reflect.TypeOf(objectVar))
15fmt.Println(reflect.TypeOf(nilVar))
string
int
float64
bool
[]string
map[string]int
- In Golang, there are methods with the same name for variable type conversions.
1floatNumber := 5.2
2intNumber := int(floatNumber)
3fmt.Println(intNumber)
5
Conditional Statements and Loops
- In the
statement, unlike in JavaScript, parentheses are not used.if-else
1if num > 50 {
2 // ...
3} else if num > 20 {
4 // ...
5} else {
6 // ...
7}
- There is a special syntax to execute a code when the function runs without errors.
1// the 'err' variable can only be accessed within this block.
2if err := someFunction(); err != nil {
3 fmt.Println(err.Error())
4}
- There are two uses of the
statement. If we want to advance to the next case, we can useswitch
.fallthrough
1// first way
2var status string = "ERROR"
3
4switch status {
5 case "WARN":
6 // ...
7 case "ERROR":
8 // ...
9 default:
10 // ...
11}
12
13// second way
14var num int = 25
15
16switch {
17 case num < 50:
18 // ...
19 case num < 100:
20 // ...
21 default:
22 // ...
23}
- There are different usages of the for loop structure. Golang does not have a built-in while loop structure.
1for i := 1; i <= 100; i++ {
2 fmt.Println(i)
3}
Function
- We use the
keyword to define a function. Unlike TypeScript, we don't use a colon when defining the parameter or return type.func
1package main
2
3import (
4 "fmt"
5 "time"
6)
7
8func main() {
9 fmt.Println(getAgeByYear(1999))
10}
11
12func getAgeByYear(yearOfBirth int) int {
13 return time.Now().Year() - yearOfBirth
14}
- We can return multiple values in functions.
1func getNumArray() (int, int) {
2 return 4, 5
3}
- We can use the
operator to collect a variable number of arguments as a collection. These functions are called variadic functions....
1func printNames(names ...string) {
2 for _, name := range names {
3 fmt.Println(name)
4 }
5}
Array
- Arrays must have either a fixed size or a size specified at initialization.
Ex,
. We can also use the[5]int != [4]int
operator if we want the size to be automatically calculated....
1var nums [5]int
2fmt.Println(nums)
3
4var nums2 [2]float64 = [2]float64{2.2, 3.8}
5fmt.Println(nums2)
6
7names := [4]string{"Ali", "Veli"}
8fmt.Println(names)
9
10names2 := [...]string{"Ali", "Veli", "Ahmet"}
11fmt.Print(names2)
12fmt.Println(" - type: ", reflect.TypeOf(names2))
[0 0 0 0 0]
[2.2 3.8]
[Ali Veli ]
[Ali Veli Ahmet] - type: [3]string
- We can use
to iterate over the elements of arrays.range
1nums := [5]int{1, 2, 3, 4, 5}
2
3for _, num := range nums {
4 fmt.Print(num)
5}
12345
- We can also use
to iterate over string values. However, it returns the byte value instead of the character. Therefore, we need to convert it to string.range
1var mySentence = "Sentence"
2
3for index, letter := range mySentence {
4 fmt.Println("Index:", index, "Byte:", letter, "Letter:", string(letter))
5}
Index: 0 Byte: 83 Letter: S
Index: 1 Byte: 101 Letter: e
Index: 2 Byte: 110 Letter: n
Index: 3 Byte: 116 Letter: t
Index: 4 Byte: 101 Letter: e
Index: 5 Byte: 110 Letter: n
Index: 6 Byte: 99 Letter: c
Index: 7 Byte: 101 Letter: e
- Dynamic-sized arrays can be defined.
is used to create objects of typemake(type, len, cap)
,slice
, ormap
.channel
1// first param: item type
2// second param: starting size
3// third param: max capacity
4var mySlice []int = make([]int, 5, 10)
5
6fmt.Println(len(mySlice))
7fmt.Println(cap(mySlice))
5
10
- To slice an array, we use the syntax of two colons.
1nums := [5]int{1, 2, 3, 4, 5}
2someNums := nums[2:4]
3fmt.Println(reflect.TypeOf(someNums))
4fmt.Println(someNums)
[]int
[3, 4]
- To add an element to a dynamic array, we use the built-in
method. It takes the array and new elements.append()
1var arr []int = make([]int, 3, 10)
2newArr := append(arr, 5, 2, 7, 12)
3
4fmt.Println(newArr)
[0 0 0 5 2 7 12]
Map
- It is a data type that holds key-value pairs, similar to objects in JavaScript. We can define it in three different ways.
1// first way
2userInfo1 := map[string]string{
3 "name": "Cem",
4 "surname": "Yılmaz",
5}
6fmt.Println(userInfo1)
7
8// second way
9userInfo2 := map[string]string{}
10userInfo2["name"] = "Cem"
11userInfo2["surname"] = "Yılmaz"
12fmt.Println(userInfo2)
13
14// third way
15var userInfo3 map[string]string = make(map[string]string)
16userInfo3["name"] = "Cem"
17userInfo3["surname"] = "Yılmaz"
18fmt.Println(userInfo3)
map[name:Cem surname:Yılmaz]
map[name:Cem surname:Yılmaz]
map[name:Cem surname:Yılmaz]
- Functions and objects usually return two values. The second value indicates whether the operation was successful or not and can also be used to check if a key exists in an object.
1userInfo := map[string]string{
2 "name": "Cem",
3 "surname": "Yılmaz",
4}
5
6city, hasCityKey := userInfo["city"]
7fmt.Println("value:", city)
8fmt.Println("hasCityKey:", hasCityKey)
9
10// if we want to execute a code only when the key is exist
11userInfo["city"] = "Istanbul"
12if city, hasCityKey := userInfo["city"]; hasCityKey {
13 fmt.Printf("%s live in %s", userInfo["name"], city)
14}
value:
hasCityKey: false
Cem live in Istanbul
- To remove a key from map, we can use
.delete()
1userInfo := map[string]string{
2 "name": "Cem",
3 "surname": "Yılmaz",
4}
5
6delete(userInfo, "surname")
7fmt.Println(userInfo)
map[name:Cem]
Package
- We can create our own package.
1package utils
2
3func Add(nums ...int) int {
4 total := 0
5
6 for _, num := range nums {
7 total += num
8 }
9
10 return total
11}
1package main
2
3import (
4 "fmt"
5 "{workspaceFolderName}/utils"
6)
7
8func main() {
9 utils.Add(2, 21)
10}
Unit Testing
- Tests of files should be located in the same directory and named with a suffix of
added to the original file name._test
1package utils
2
3func Average(nums ...int) int {
4 total := 0
5
6 for _, num := range nums {
7 total += num
8 }
9
10 return total
11}
1package utils
2
3import (
4 "testing"
5)
6
7func TestAverage(t *testing.T) {
8 expected := 4
9 actual := Average(3, 2)
10
11 if actual != expected {
12 t.Errorf(
13 "Add function does not add up: Expected: %d, Actual: %d",
14 expected,
15 actual
16 )
17 }
18}