From 843f33dd86d6ca1a2138bd2f352809d3883e3492 Mon Sep 17 00:00:00 2001 From: Pradumna Saraf Date: Wed, 18 Sep 2024 12:25:15 +0530 Subject: [PATCH] docs: Refactor Golang notes (#98) --- docs/golang/concepts/0) test.go | 8 +- docs/golang/concepts/11) slices.go | 1 - docs/golang/concepts/12) slices2.go | 3 +- docs/golang/concepts/13) map.go | 10 +- docs/golang/concepts/20) methods.go | 24 ++- docs/golang/concepts/21) defer.go | 2 + docs/golang/concepts/22) map-ad.go | 6 +- docs/golang/concepts/23) files.go | 16 +- docs/golang/concepts/24) handling-web-req.go | 5 +- docs/golang/concepts/26) http-requests.go | 1 - docs/golang/concepts/27) json.go | 109 +++++----- docs/golang/concepts/28) go-routine.go | 23 +-- docs/golang/concepts/30) waitgroup-api-eg.go | 12 +- docs/golang/concepts/32) channels.go | 7 +- docs/golang/concepts/33) channels-open.go | 35 ++-- docs/golang/concepts/34) Interface.go | 43 ++-- docs/golang/concepts/8) pointers.go | 10 + docs/golang/introduction.md | 202 +++++++++++++++++-- 18 files changed, 356 insertions(+), 161 deletions(-) diff --git a/docs/golang/concepts/0) test.go b/docs/golang/concepts/0) test.go index f829cd1..85f0393 100644 --- a/docs/golang/concepts/0) test.go +++ b/docs/golang/concepts/0) test.go @@ -1,7 +1 @@ -package main - -import "fmt" - -func main() { - fmt.Println("Hello World!") -} \ No newline at end of file +package main \ No newline at end of file diff --git a/docs/golang/concepts/11) slices.go b/docs/golang/concepts/11) slices.go index 40bbe8f..416fb5e 100644 --- a/docs/golang/concepts/11) slices.go +++ b/docs/golang/concepts/11) slices.go @@ -9,7 +9,6 @@ func main() { var fruits []string // We don't need to specify the size of the array // fruits := []string{} - We can also use this syntax to create a slice // fruits := make([]string, 2) - We can also use make function to create a slice - fruits = append(fruits, "Apple") fruits = append(fruits, "Orange") fruits = append(fruits, "Banana") diff --git a/docs/golang/concepts/12) slices2.go b/docs/golang/concepts/12) slices2.go index fd67fb5..0ba33ae 100644 --- a/docs/golang/concepts/12) slices2.go +++ b/docs/golang/concepts/12) slices2.go @@ -5,9 +5,10 @@ import "fmt" func main() { // remove an element from a slice var cars = []string{"BMW", "Audi", "Mercedes", "Ford", "Fiat"} - + fmt.Println(cars) var index int = 2 + // We are concatenating two slice and rewriting the 1st Slice. cars = append(cars[:index], cars[index+1:]...) // remove the element at index 2. :index is from 0 to index-1, [index+1:] is from index+1 to end. We can append both elements as well as a slice. ... is to denote a slice fmt.Println(cars) diff --git a/docs/golang/concepts/13) map.go b/docs/golang/concepts/13) map.go index 26a43b7..b66a57e 100644 --- a/docs/golang/concepts/13) map.go +++ b/docs/golang/concepts/13) map.go @@ -5,8 +5,8 @@ import ( ) func main() { - // var languages = map[string]string{} - var languages = make(map[string]string, 0) // create a slice of maps with a length of 0. // map[keyType]valueType + var languages = map[string]string{} + // var languages = make(map[string]string, 0) // create a slice of maps with a length of 0. // map[keyType]valueType //laguages[key] = value languages["JS"] = "JavaScript" @@ -17,10 +17,10 @@ func main() { fmt.Println(languages) fmt.Println(languages["JS"]) // access the value of a key - delete(languages, "RB") // delete a key from a map + delete(languages, "RB") // Deleting a Key (No key means no Value as well) fmt.Println(languages) - for _, value := range languages { - fmt.Println(value) + for key, value := range languages { + fmt.Println(key, value) } } diff --git a/docs/golang/concepts/20) methods.go b/docs/golang/concepts/20) methods.go index 283627e..c7942eb 100644 --- a/docs/golang/concepts/20) methods.go +++ b/docs/golang/concepts/20) methods.go @@ -2,26 +2,30 @@ package main import "fmt" -type UserData struct { // struct is a collection of fields. +type UserData struct { fistName string lastName string email string numberOfTickets int } +/* +func (receiver Type) MethodName(parameters) returnType { + // method body +} +*/ + +func (u UserData) greetUser() { + fmt.Println("Hello", u.fistName, u.lastName) +} + func main() { - user := UserData{ // struct initialization + user := UserData{ fistName: "John", lastName: "Doe", email: "pradumnasaraf@gmail.com", numberOfTickets: 2, } - - fmt.Println(user) - - user.greetUser() // Method call -} - -func (u UserData) greetUser() { // method with receiver - fmt.Println("Hello", u.fistName, u.lastName) + + user.greetUser() // Call the method } diff --git a/docs/golang/concepts/21) defer.go b/docs/golang/concepts/21) defer.go index c8b3100..d6c9fab 100644 --- a/docs/golang/concepts/21) defer.go +++ b/docs/golang/concepts/21) defer.go @@ -2,6 +2,8 @@ package main import "fmt" + +// Defer statements are run at the end func main() { defer fmt.Println("This is the first defer statement") defer fmt.Println("This is the second defer statement") diff --git a/docs/golang/concepts/22) map-ad.go b/docs/golang/concepts/22) map-ad.go index 3a3fe08..e074e50 100644 --- a/docs/golang/concepts/22) map-ad.go +++ b/docs/golang/concepts/22) map-ad.go @@ -6,8 +6,8 @@ import ( ) func main() { - var bookings = make([]map[string]string, 0) // This is a slice of map. - // var bookings = []map[string]string{} // This is also valid + + bookings :=[]map[string]string{} // This is a slice of map. for i := 0; i < 1; i++ { @@ -15,7 +15,7 @@ func main() { age := 32 city := "New York" - var myMap = make(map[string]string) + myMap := map[string]string{} myMap["name"] = name myMap["age"] = strconv.FormatInt(int64(age), 10) diff --git a/docs/golang/concepts/23) files.go b/docs/golang/concepts/23) files.go index 60c3187..45f581a 100644 --- a/docs/golang/concepts/23) files.go +++ b/docs/golang/concepts/23) files.go @@ -8,17 +8,16 @@ import ( func main() { - // Process to create, write and read a file - content := "Hey I am Pradumna" - //"." means current directory + // Create a file file, err := os.Create("./hello.txt") if err != nil { panic(err) } + // Writing content in a file length, err := io.WriteString(file, content) if err != nil { @@ -27,6 +26,7 @@ func main() { fmt.Println("Length:", length) + // Reading a file data, err := os.ReadFile("./hello.txt") if err != nil { @@ -34,6 +34,14 @@ func main() { } - // data is of type []byte so we need to convert it to string + // Print the read data fmt.Println(string(data)) + + //Removing a file + err = os.Remove("./hello.txt") + + if err != nil { + panic(err) + + } } diff --git a/docs/golang/concepts/24) handling-web-req.go b/docs/golang/concepts/24) handling-web-req.go index 71f9829..01aa92c 100644 --- a/docs/golang/concepts/24) handling-web-req.go +++ b/docs/golang/concepts/24) handling-web-req.go @@ -15,11 +15,12 @@ func main() { fmt.Printf("Response is of type: %T\n", res) fmt.Println(res.Status) + fmt.Println(res.Proto) - databytes, err := io.ReadAll(res.Body) //We can't read the response directly. So we use ioutil.ReadAll() + dataBytes, err := io.ReadAll(res.Body) //We can't read the response directly. So we use ioutil.ReadAll() checkNilError(err) - content := string(databytes) // convert the byte array to string + content := string(dataBytes) // convert the byte array to string fmt.Println(content) } diff --git a/docs/golang/concepts/26) http-requests.go b/docs/golang/concepts/26) http-requests.go index b18b87f..83ee2b5 100644 --- a/docs/golang/concepts/26) http-requests.go +++ b/docs/golang/concepts/26) http-requests.go @@ -3,7 +3,6 @@ package main import ( "fmt" "io" - "io/ioutil" "log" "net/http" "net/url" diff --git a/docs/golang/concepts/27) json.go b/docs/golang/concepts/27) json.go index 2133ed7..897cb6b 100644 --- a/docs/golang/concepts/27) json.go +++ b/docs/golang/concepts/27) json.go @@ -6,77 +6,92 @@ import ( "log" ) +// It's important to mention `json:"key-name"` otherwise it will pick the keyword mentioned in the Struct with capitalization. type Person struct { - Name string `json:"coursename"` - Age int `json:"age"` - DOBY int `json:"doby"` + Name string `json:"name"` + Age int `json:"age"` + Place string `json:"place"` } func main() { - - // structToJson() - // jsonToStruct() + // letsMarshal() + // letsUnMarshal() extractJsonData() } -func structToJson() { - tom := []Person{ +func letsUnMarshal() { + jsonData := []byte(` { - Name: "Tom", - Age: 21, - DOBY: 199, + "name": "Pradumna Saraf", + "age": 25, + "place": "India" + } + `) + + var myData Person + + isJSONValid := json.Valid(jsonData) + if isJSONValid == true { + json.Unmarshal(jsonData, &myData) + } else { + fmt.Println("Invalid JSON") + } + + fmt.Println(myData.Age) + fmt.Println(myData.Name) + fmt.Println(myData.Place) + + +} + +func letsMarshal() { + myPerson := Person{ + Name: "Pradumna Saraf", + Age: 25, + Place: "India", + } + + myPersonSlice := []Person{ + { + Name: "Pradumna Saraf", + Age: 25, + Place: "India", }, { - Name: "Ben", - Age: 21, - DOBY: 199, + Name: "Pradumna Saraf", + Age: 25, + Place: "India", }, { - Name: "Loft", - Age: 21, - DOBY: 199, + Name: "Pradumna Saraf", + Age: 25, + Place: "India", }, } - // Marshal will convert the struct to json - jsonOutput, err := json.MarshalIndent(tom, "", " ") + // Single Object + data, err := json.Marshal(&myPerson) if err != nil { - log.Fatal(err) - + log.Fatal("Unable to Marshal") } + fmt.Println(string(data)) - fmt.Println(string(jsonOutput)) -} - -func jsonToStruct() { - jsonData := []byte(`{ - "coursename": "ben", - "age": 200, - "doby": 1975 -}`) - - var myUser Person - - validateJson := json.Valid(jsonData) - if validateJson == true { - // Unmarshal will convert the json to struct - json.Unmarshal(jsonData, &myUser) - fmt.Println(myUser) - } else { - fmt.Println("JSON is invalid") + // A list of Objects + sliceData, err := json.MarshalIndent(&myPersonSlice, "", " ") + if err != nil { + log.Fatal("Unable to Marshal") } - + fmt.Println(string(sliceData)) } -func extractJsonData() { +func extractJsonData() { jsonData := []byte(` { - "coursename": "Mern", - "price": 200, - "website": "yt", - "tags": [ "full-stack", "js" ] + "name": "Pradumna Saraf", + "age": 25, + "place": "India" } `) @@ -89,4 +104,4 @@ func extractJsonData() { fmt.Printf("Key is %v with the value %v and type is: %T\n", k, v, v) } -} +} \ No newline at end of file diff --git a/docs/golang/concepts/28) go-routine.go b/docs/golang/concepts/28) go-routine.go index 29fb1ca..8d3b349 100644 --- a/docs/golang/concepts/28) go-routine.go +++ b/docs/golang/concepts/28) go-routine.go @@ -6,8 +6,9 @@ import ( ) func main() { - go greeter("Hello") - greeter("World") + go greeter("First Statement") + go greeter("Second Statement") + greeter("Third Statement") } @@ -17,22 +18,4 @@ func greeter(s string) { time.Sleep(1 * time.Second) fmt.Println(s) } - - //runThisInMain() -} - -func runThisInMain() { - for { - var input string - go sayHello() // go keyword is used to create a go routine. This is a concurrent execution of the function, meaning it will run in the background and will not block the main thread. - fmt.Print("Enter Text: ") - fmt.Scanln(&input) - fmt.Println("Your Input was:", input) - } -} - -func sayHello() { - time.Sleep(5 * time.Second) - fmt.Println("Hello from sayHello") - } diff --git a/docs/golang/concepts/30) waitgroup-api-eg.go b/docs/golang/concepts/30) waitgroup-api-eg.go index 53ecd95..c5ba21b 100644 --- a/docs/golang/concepts/30) waitgroup-api-eg.go +++ b/docs/golang/concepts/30) waitgroup-api-eg.go @@ -6,9 +6,9 @@ import ( "sync" ) -var wg sync.WaitGroup //pointer -var mut sync.Mutex //pointer -var signals = []string{"test"} +var wg sync.WaitGroup +var mut sync.Mutex +var statusCodes = []int{} func main() { @@ -25,10 +25,10 @@ func main() { for _, endpoint := range endpoints { go checkStausCode(endpoint) wg.Add(1) - } + } wg.Wait() - fmt.Print(signals) + fmt.Print(statusCodes) } func checkStausCode(endpoint string) { @@ -39,7 +39,7 @@ func checkStausCode(endpoint string) { fmt.Println("Problem in the endpoint", endpoint) } else { mut.Lock() - signals = append(signals, endpoint) + statusCodes = append(statusCodes, res.StatusCode) mut.Unlock() fmt.Printf("Status code for %s is %d\n", endpoint, res.StatusCode) } diff --git a/docs/golang/concepts/32) channels.go b/docs/golang/concepts/32) channels.go index 0224611..199dec1 100644 --- a/docs/golang/concepts/32) channels.go +++ b/docs/golang/concepts/32) channels.go @@ -6,14 +6,15 @@ import ( ) func main() { - myChannel := make(chan int, 2) // buffered channel. By default, it is unbuffered + + myChannel := make(chan int, 2) // buffered channel + // var myChannel chan int // unbuffered channel wg := &sync.WaitGroup{} + wg.Add(2) go func(ch chan int, wg *sync.WaitGroup) { fmt.Println(<-ch) // read from channel - - wg.Done() }(myChannel, wg) diff --git a/docs/golang/concepts/33) channels-open.go b/docs/golang/concepts/33) channels-open.go index 5a21d7a..8cd63fc 100644 --- a/docs/golang/concepts/33) channels-open.go +++ b/docs/golang/concepts/33) channels-open.go @@ -6,25 +6,30 @@ import ( ) func main() { - // When channel is closed, the value read from the channel is the zero value and at the same time maybe the write value can also be zero value. - // So we need to check if the channel is closed or not by using the second return value of the read operation. - myChannel := make(chan int, 2) wg := &sync.WaitGroup{} + myCh := make(chan int, 1) wg.Add(2) - go func(ch <-chan int, wg *sync.WaitGroup) { - val, isChannelOpen := <-ch - fmt.Println(val, isChannelOpen) - wg.Done() - }(myChannel, wg) - - go func(ch chan<- int, wg *sync.WaitGroup) { - // ch <- 5 uncomment this line to see the difference - close(myChannel) - wg.Done() - - }(myChannel, wg) + go func(channel chan int, wait *sync.WaitGroup) { + fmt.Println("First Go Routine") + wait.Done() + + value, isChannelOpen := <-myCh + + if !isChannelOpen { + fmt.Println("Channel Closed") + } + fmt.Println(value) + }(myCh, wg) + + go func(channel chan int, wait *sync.WaitGroup) { + fmt.Println("Second Go Routine") + // myCh <- 3 + close(myCh) + wait.Done() + // myCh <- 5 + }(myCh, wg) wg.Wait() } diff --git a/docs/golang/concepts/34) Interface.go b/docs/golang/concepts/34) Interface.go index d5d1bf8..f9c2a54 100644 --- a/docs/golang/concepts/34) Interface.go +++ b/docs/golang/concepts/34) Interface.go @@ -1,39 +1,36 @@ package main -import "fmt" +import ( + "fmt" + "math" +) -type Printer interface { - printInfo() +type shape interface { + area() float64 } - - -type Book struct { - Title string - Price float32 +type circle struct { + radius float64 } -type Drink struct { - Name string - Price float32 +type rectangle struct { + width float64 + length float64 } -func (b Book) printInfo() { - fmt.Println("Book Title:", b.Title, "Price:", b.Price) +func (r rectangle) area() float64 { + return r.length * r.width } -func (d Drink) printInfo() { - fmt.Println("Drink Name:", d.Name, "Price:", d.Price) +func (c circle) area() float64 { + return 2 * math.Pi * c.radius } func main() { - book := Book{Title: "The Alchemist", Price: 9.99} - drink := Drink{Name: "Coke", Price: 1.99} - - // book.printInfo() - // drink.printInfo() + myRectangle := rectangle{width: 8, length: 10} + fmt.Println(myRectangle.area()) - info := []Printer{book, drink} + myCircle := circle{radius: 10} + fmt.Println(myCircle.area()) - info[0].printInfo() - info[1].printInfo() + } diff --git a/docs/golang/concepts/8) pointers.go b/docs/golang/concepts/8) pointers.go index 2c2d89b..768b9a8 100644 --- a/docs/golang/concepts/8) pointers.go +++ b/docs/golang/concepts/8) pointers.go @@ -10,5 +10,15 @@ func main(){ println("myInt:", ptr) // ptr is the address of the variable println("myInt:", *ptr) // * is used to get the value stored at the address + +} +type Persons struct { + Name string + Age int +} + + +func ModifyPerson(p *Persons) { + p.Name = "Alice" } \ No newline at end of file diff --git a/docs/golang/introduction.md b/docs/golang/introduction.md index 6bb23f0..f249612 100644 --- a/docs/golang/introduction.md +++ b/docs/golang/introduction.md @@ -401,7 +401,29 @@ func(x, y int) int { ### Methods -- A method is a function with a special receiver argument. The receiver appears in its own argument list between the func keyword and the method name. +A method is a function with a special receiver argument. The receiver appears in its own argument list between the func keyword and the method name. Receiver can be of any type. Receiver is a special type of parameter that is passed to a method. It is similar to `this` in other languages. It is used to access the fields and methods associated with the Type like a Struct. There are two types of receivers: + +1. **Value Receiver**: It is used when we don't want to modify the original value. + +> `func (t Test) printName() { fmt.Println(t.Name) }` + +2. **Pointer Receiver**: It is used when we want to modify the original value. + +> `func (t *Test) printName() { fmt.Println(t.Name) }` + +Here's what a receiver does: + +### Example from your code: + +```go +func (t Test) printName() { + fmt.Println(t.Name) +} +``` + +## Methods + +Methods are functions that are associated with a type. They are similar to functions but are defined with a receiver. The receiver is like a parameter. It is the first argument of the method. ```go type Person struct { @@ -516,10 +538,42 @@ type Person struct { ### Go routines +#### Concurrency vs Parallelism + +- **Concurrency** - It is the ability of a program to be decomposed into parts that can run independently of each other. It is the composition of independently executing processes. It is about dealing with lots of things at once. + +- **Parallelism** - It is the ability of a program to run multiple tasks simultaneously. It is about doing lots of things at once. + - A goroutine is a lightweight thread managed by the Go runtime. We can create a goroutine using the keyword `go`. It is similar to threads in other languages. The purpose of a goroutine is to run a function concurrently with other functions. It is a function that is capable of running concurrently with other functions. It will not wait for the function to complete. It will execute the function concurrently. +```go +func main() { + p := Person{Name: "John", Age: 25} + + data, err := json.Marshal(p) + if err != nil { + fmt.Println(err) + return + } + fmt.Println(string(data)) + + var p1 Person + err = json.Unmarshal(data, &p1) + if err != nil { + fmt.Println(err) + return + } + fmt.Println(p1) +} +``` + + +## Go routines and WaitGroup + +- A goroutine is a lightweight thread managed by the Go runtime. We can create a goroutine using the keyword `go`. It is similar to threads in other languages. The purpose of a goroutine is to run a function concurrently with other functions. + ```go go func() { fmt.Println("Hello") @@ -528,10 +582,12 @@ go func() { - If the main function exits, the program will exit immediately even if the goroutine is still running. To prevent this, we can use the `WaitGroup` type. For Eg: -```go - #### WaitGroup +- A WaitGroup waits for a collection of goroutines to finish. The main function will wait for the goroutines to finish before exiting. + +```go + var wg sync.WaitGroup func main() { @@ -586,40 +642,160 @@ type Person struct { - `make()` - It is used to create a channel. It takes the type of the channel as an argument. +```go +ch := make(chan int) + +go func() { + ch <- 10 +}() + +val := <-ch +fmt.Println(val) +``` + We can create a buffered channel by passing the buffer size as the second argument to the `make()` function. By default, the channel is unbuffered and can only hold one value. So, if we try to send multiple value to the channel it will give an error. + +### Buffered Channel + +A buffered channel is a channel with a buffer. It can hold multiple values. We can specify the buffer size when we create the channel. + +```go +var ch = make(chan int, 5) // buffered channel with a buffer size of 5 +``` + +### Unbuffered Channel + +An unbuffered channel is a channel without a buffer. It can hold only one value. We can send a value to the channel only if there is a goroutine ready to receive the value. + ```go var ch = make(chan int) // unbuffered channel +``` + +### Closing a Channel -var ch = make(chan int, 10) // buffered channel +We can close a channel using the `close()` function. It is used to indicate that no more values will be sent on the channel. + +```go +msg := make(chan int) + +go func() { + msg <- 1 + close(msg) // After closing the channel we can't send any more values +}() ``` +But here a catch even tho channel is closed we can still receive the values from it like zero, so it's dalmatic whether the it's channel is closed or the value is zero. To overcome this we can receive the value and a boolean value which will tell us whether the channel is closed or not. + ```go -ch := make(chan int) +package main -ch <- 10 // It will send 10 to the channel +import ( + "fmt" + "sync" +) -<- ch // It will receive from the channel -val, ok := <- ch // It will receive from the channel and check if the channel is closed or not -``` +var wait = sync.WaitGroup{} + +func main() { -- `close()` - It is used to close a channel. It takes the channel as an argument. + myChannel := make(chan int, 1) + + wait.Add(2) + go func() { + fmt.Println("First Go Routine") + wait.Done() + + hello, isChannelOpen := <- myChannel + + if !isChannelOpen { + fmt.Println("Channel Closed") + } + fmt.Println(hello) + }() + + go func() { + fmt.Println("Second Go Routine") + close(myChannel) + wait.Done() + // myChannel <- 5 + }() + + wait.Wait() +} +``` -#### Send Only Channel +### Send Only Channel ```go var ch = make(chan<- int) // send only channel ``` -Receive Only Channel +```go +go func (ch chan<- int) { + ch <- 10 +}(ch) +``` + +### Receive Only Channel ```go var ch = make(<-chan int) // receive only channel ``` +```go +go func (ch <-chan int) { + val := <-ch + fmt.Println(val) +}(ch) +``` + +### IIF's (Immediately Invoked Functions) + +- An immediately invoked function is a function that is executed as soon as it is created. It is a function that is executed immediately after it is created. It is also known as a self-invoking function. + +```go +func main() { + func() { + fmt.Println("Hello") + }() +} +``` +## Error Handling + +In Go, errors are values. We can use the `error` type to represent an error. We can use the `errors.New` function to create a new error. It returns an error. + +```go +func divide(x, y int) (int, error) { + if y == 0 { + return 0, errors.New("division by zero") + } + return x / y, nil +} +``` + +### Code Organization + +We can organize our code by putting functions/ variables in different files and can use them in the main file or calling the main function from other files. + +Also, we can have multiple packages in a single directory. + +### Expoting and Importing + +- We can export a function/ variable by capitalizing the first letter of the function/ variable name. Now we can use it in other packages. + +```go +var mutex sync.Mutex + +mutex.Lock() + +// critical section + +mutex.Unlock() +``` + ### What's next? - [Learning Resources](./learning-resources.md) - Learn more about Golang with these resources. - [Other Resources](./other-resources.md) - A list of resources to learn more about Golang. -- \ No newline at end of file