├── README.md └── simple-hello-world.go /README.md: -------------------------------------------------------------------------------- 1 | # Go - Golang quick intro study notes. 📝💨 2 | 3 | This is my study notes for Golang. It is a quick intro/guide to start with golang if you have prior programming experience. 4 | 5 | ## Table of content 6 | - [🏓Go Fundmentals](#--go-fundmentals) 7 | * [Notes](#notes) 8 | * [Variables Declaration](#variables-declaration) 9 | * [Go Primitive Types](#go-primitive-types) 10 | * [Visibility](#visibility) 11 | * [Take Input from console](#take-input-from-console) 12 | * [Iota](#iota) 13 | * [Go Pointers](#go-pointers) 14 | * [Go If-Conditions](#go-if-conditions) 15 | * [Go For-Loops](#go-for-loops) 16 | * [Go For-Each loop](#go-for-each-loop) 17 | * [Go Switch Statement](#go-switch-statement) 18 | * [Type Function and Returning Functions](#type-function-and-returning-functions) 19 | - [✨Go functions and return types.](#-go-functions-and-return-types) 20 | * [Notes](#notes-1) 21 | * [Typical Function](#typical-function) 22 | * [Multiple Returns](#multiple-returns) 23 | * [Named Returns](#named-returns) 24 | * [Variadic Functions / Variable arguments list](#variadic-functions---variable-arguments-list) 25 | * [Type Function and Returning Functions](#type-function-and-returning-functions-1) 26 | * [Callbacks - Passing functions as argument](#callbacks---passing-functions-as-argument) 27 | * [Defer Keyword](#defer-keyword) 28 | * [Receivers](#receivers) 29 | * [Overriding Receivers](#overriding-receivers) 30 | - [🏗Go Data-Structures](#--go-data-structures) 31 | * [Arrays](#arrays) 32 | * [Slices](#slices) 33 | * [Iterating over a Slice](#iterating-over-a-slice) 34 | * [Appending to Slice](#appending-to-slice) 35 | * [Common Slice Functions](#common-slice-functions) 36 | * [Slices Tricks](#slices-tricks) 37 | * [Maps](#maps) 38 | - [🏢Go Structs / OOP](#--go-structs---oop) 39 | * [Notes](#notes-2) 40 | * [Go supports](#go-supports) 41 | + [Encapsulation](#encapsulation) 42 | + [Inheritance and Reusability](#inheritance-and-reusability) 43 | + [Polymorphism and Interfaces](#polymorphism-and-interfaces) 44 | + [Overriding](#overriding) 45 | - [🥂Go Concurrency](#--go-concurrency) 46 | * [Intro](#intro) 47 | * [Notes](#notes-3) 48 | * [mutex Locks, WaitGroups, and Atomic operations](#mutex-locks--waitgroups--and-atomic-operations) 49 | * [Go Channels](#go-channels) 50 | + [Example 1](#example-1) 51 | + [Example 2](#example-2) 52 | + [Example 3](#example-3) 53 | + [Example 4](#example-4) 54 | + [Example 5 - Semaphores](#example-5---semaphores) 55 | + [Example 6 - Using channels as arguments/returns](#example-6---using-channels-as-arguments-returns) 56 | - [🐞Go Error Handling](#--go-error-handling) 57 | * [Notes](#notes-4) 58 | * [Example](#example) 59 | 60 | 61 | -------------------------------------------------------------------------- 62 | 63 | 64 | # 🏓Go Fundmentals 65 | 66 | ## Notes 67 | 68 | 1. Program Excuetable is at `package main/func main()` 69 | ``` Go 70 | 71 | package main //Excuetables must be of package main 72 | import "fmt" 73 | 74 | func main() { 75 | var g string = "Hello golang" 76 | println(g) 77 | } 78 | 79 | func function() string { 80 | return "Five of Diamonds" 81 | } 82 | ``` 83 | 84 | 2. **Blank Identifier**: Use `_` to replace unused variables. 85 | 3. there are two primitive ways to allocate to a pointer, `new()` and `make()`, they differ though, we will discuss that later, briefly new returns pointer, make return value. read [Doc](https://golang.org/doc/effective_go.html#allocation_new). 86 | 4. Every thing is `passed by value` except **arrays, slices, maps and channels** which some calls r**eference types**, these types are passed by reference ( they internally have pointers, so no copying of the actual data happens when passing them) . 87 | 5. Unlike in C, it's perfectly OK to return the address of a local variable; the storage associated with the variable survives after the function returns. 88 | 89 | ## Variables Declaration 90 | 91 | ``` Go 92 | 93 | //Declration 94 | var g string 95 | 96 | //Assignment 97 | g = "golang" 98 | 99 | //Declration & Assignment 100 | var g = "golang" 101 | var g string = "golang" 102 | 103 | //Shorthand - Declration & Assignmnet 104 | a := 10 105 | b := "golang" 106 | ``` 107 | 108 | *Uninitialized variables are given its zero value *(e.g int = 0, string = "", bool = false)* 109 | 110 | ## Go Primitive Types 111 | 112 | The possible values for `bool` are `true` and `false`. 113 | 114 | - **uint8** : unsigned 8-bit integers `(0 to 255)` 115 | - **uint16** : unsigned 16-bit integers `(0 to 65535)` 116 | - **uint32** : unsigned 32-bit integers `(0 to 4294967295)` 117 | - **uint64** : unsigned 64-bit integers `(0 to 18446744073709551615)` 118 | - **int8** : signed 8-bit integers (`-128 to 127)` 119 | - **int16** : signed 16-bit integers `(-32768 to 32767)` 120 | - **int32** : signed 32-bit integers `(-2147483648 to 2147483647)` 121 | - **int64** : signed 64-bit integers `(-9223372036854775808 to 9223372036854775807)` 122 | - **int** is either **int64** or **int32** depends on the implementation. 123 | - **float32** : set of `all IEEE-754 32-bit` floating-point numbers 124 | - **float64** : set of `all IEEE-754 64-bit` floating-point numbers 125 | - **complex64** the set of all complex numbers with float32 real and imaginary parts 126 | - **complex128** the set of all complex numbers with float64 real and imaginary parts 127 | - byte alias for `uint8` rune alias for `int32` 128 | 129 | ## Visibility 130 | 131 | If `variables`/`functions` starts with Uppercase character, it is accessible outside the scope of its package, if lowercase then it is only accessible inside its package. 132 | 133 | ``` Go 134 | package myPkg 135 | 136 | var Uppercase = "This is accessible outside the pkg" 137 | var lowercase = "This is not accessible outside the pkg" 138 | func UppercaseFunc() string { return "This is accessible outside the pkg"} 139 | func lowercaseFunc() string { return "This is accessible outside the pkg"} 140 | 141 | // Another file: 142 | package main 143 | import "myPkg" 144 | 145 | func main() { 146 | //Accessible 147 | println(myPkg.Uppercase) 148 | println(myPkg.UppercaseFunc()) 149 | 150 | //Not Accessible 151 | println(myPkg.lowercase) 152 | println(myPkg.lowercaseFunc()) 153 | } 154 | ``` 155 | ## Take Input from console 156 | ``` Go 157 | //take input like cin >> in c++ 158 | var x int = 1337 159 | var y string = "string value" 160 | 161 | _, err := fmt.Scan(&x, &y) 162 | fmt.Println("You Entered x:", x, " and y: ", y, " Error: ", err) 163 | 164 | //take input like scanf in C 165 | _, err = fmt.Scanf("%d %s", &x, &y) 166 | fmt.Println("You Entered x:", x, " and y: ", y, " Error: ", err) 167 | 168 | //take input with white spaces 169 | var z string = "string" 170 | 171 | scanner := bufio.NewScanner(os.Stdin) 172 | 173 | scanner.Scan() 174 | 175 | z = scanner.Text() 176 | 177 | fmt.Println("You Entered z:", z) 178 | ``` 179 | ## Iota 180 | 181 | iota in Go, is a value used within the **const** block, its value starts at 0 per block, and increment each time it is used again 182 | 183 | ``` Go 184 | const ( 185 | c0 = iota // c0 == 0 186 | c1 = iota // c1 == 1 187 | c2 = iota // c2 == 2 188 | ) 189 | ``` 190 | 191 | ## Go Pointers 192 | 193 | Pointers syntax is essentially like C/C++ 194 | ``` Go 195 | var value int = 1000 196 | var pointer *int = &value 197 | println(value) //1000 198 | println(pointer) //0xfffffffff 199 | println(*pointer) //1000 200 | (*pointer)++ //1001 201 | *pointer = *pointer + 10 //1011 202 | println(*pointer) //1011 203 | println(*pointer + *pointer) //1011 + 1011 = 2022 204 | ``` 205 | 206 | ## Go If-Conditions 207 | 208 | - Braces must open in the same line of the if/else ( Aghh 😕 ) 209 | - in Go's if-statements **parentheses( )** around conditions **are optional**. but the **braces { } are required** even for oneliners. 210 | ``` Go 211 | value := 10 212 | if value < 10 { 213 | println("Less Than 10") 214 | } else if value > 10 { 215 | println("Greater Than 10") 216 | } else { 217 | println("Equals 10") 218 | } 219 | 220 | //if conditions with statment 221 | //note that value is inscope of all if/else's 222 | if value := 10; value < 10 { 223 | println(value, "Less Than 10") 224 | } else if value > 10{ 225 | println(value, "Greater Than 10") 226 | }else{ 227 | println(value, "Equals 10") 228 | } 229 | ``` 230 | 231 | Go doesn't have Ternary Operator ( x < 0 ? A : B ) 🤷 232 | 233 | ## Go For-Loops 234 | 235 | There are **3 forms** of for loops, also **there is no a while loop syntax in GO** (instead it is a form of for loops), also there is no do-while at all 236 | ``` Go 237 | //For loop 238 | for j := 7; j <= 9; j++ { /*stuff*/ } 239 | 240 | //While like for loop 241 | i := 1 242 | for i <= 3 { /*stuff*/ i++ } 243 | 244 | //Infinite Loop : While(true) 245 | for { /*stuff*/ if (/*stuff*/) break } 246 | ``` 247 | ## Go For-Each loop 248 | ``` Go 249 | for i, v := range arr { //do stuff } 250 | for _, v := range arr { //do stuff } 251 | for i, _ := range arr { //do stuff } 252 | ``` 253 | ## Go Switch Statement 254 | 255 | Switch statements in GO doesn't require `break`; they will break by default, `fallthrough` keyword used to go to NEXT statement even if condition doesn't match, `fallthrough` is like a break so no code can be after it. however a workaround is to use `labels` and `goto` 256 | ```Go 257 | i := 2 258 | fmt.Println("Switch for i = ", i, " goes to: ") 259 | switch i { 260 | case 1: 261 | fmt.Println("one") 262 | case 2: 263 | fmt.Println("two") 264 | i = 4 265 | fallthrough //goes to NEXT case even if doesn't match. 266 | case 3: 267 | fmt.Println("three") 268 | case 4: 269 | fmt.Println("four") 270 | case 5,6: 271 | fmt.Println("five or six") 272 | default: 273 | fmt.Println("default") 274 | } 275 | ``` 276 | ## Type Function and Returning Functions 277 | 278 | 1. Functions can be assigned to variables `func0 := func() int {x++; return x}` 279 | 2. Functions that are returned from another functions has its own scope per returned function (yea ikr ? 🤷). 280 | ``` Go 281 | 282 | package main 283 | 284 | var x = 0 285 | 286 | func main() { 287 | //local x 288 | x := 0 289 | 290 | func0 := func() int {x++; return x} 291 | func1 := incrementGlobalX //without () 292 | func2 := wrapper() 293 | func3 := wrapper() 294 | 295 | println(func0(), " : func0 (local x)") 296 | println(func1(), " : func1 (global x)") 297 | println(func2(), " : func2 (per func scope x1)") 298 | println(func3(), " : func3 (per func scope x2)") 299 | println("Second Increment") 300 | println(func0(), " : func0 (local x)") 301 | println(func1(), " : func1 (global x)") 302 | println(func2(), " : func2 (per func scope x1)") 303 | println(func3(), " : func3 (per func scope x2)") 304 | } 305 | 306 | func incrementGlobalX() int { 307 | x++ 308 | return x 309 | } 310 | 311 | func wrapper() func() int { 312 | x := 0 313 | return func() int { 314 | x++ 315 | return x 316 | } 317 | } 318 | ``` 319 | 320 | 321 | -------------------------------------------------------------------------- 322 | 323 | 324 | 325 | # ✨Go functions and return types. 326 | 327 | ## Notes 328 | 329 | - Again Everything is `passed by value` except **arrays, slices, maps and channels** which some calls r**eference types**, these types are passed by reference. 330 | - unlike in C, it's perfectly OK to return the address of a local variable; the storage associated with the variable survives after the function returns. 331 | 332 | ## Typical Function 333 | ``` Go 334 | // return void 335 | func add(x int, y int) { 336 | fmt.Println("Hello, World!") 337 | } 338 | 339 | //-------arguments------return------ 340 | func add(x int, y int) int { 341 | return x + y 342 | } 343 | 344 | //-----same type arguments----------- 345 | func add(x, y int) int { 346 | return x + y 347 | } 348 | ``` 349 | 350 | ## Multiple Returns 351 | ```Go 352 | func swap(x, y string) (string, string) { 353 | return y, x 354 | } 355 | 356 | //in main 357 | a, b := swap("hello", "world") 358 | fmt.Println(a, b) //prints "world hello" 359 | ``` 360 | ## Named Returns 361 | 362 | You can declare return variables and name them at the beginning, they are returned in the end. 363 | 364 | *you can override the returns and return whatever you want at the return statement. 365 | ```Go 366 | //Returns x,y at the end. 367 | func split(sum int) (x, y int) { 368 | x = sum * 4 / 9 369 | y = sum - x 370 | return 371 | //return a,b <- u can override default return of x,y. 372 | } 373 | ``` 374 | ## Variadic Functions / Variable arguments list 375 | 376 | The rightmost argument can be a list of variable size (*slice*) of data. 377 | ```Go 378 | // x value here has no use for average. just illustrates the idea of having arguments 379 | // then a variable number of arguments 380 | func average(x int, values ...int) float64{ 381 | //print values 382 | fmt.Println("Single argument value: ", x) 383 | fmt.Println("Variable argument values: ", values) 384 | 385 | //calculate average 386 | total := 0 387 | for _, value := range values { 388 | total += value 389 | } 390 | 391 | return float64(total) / float64(len(values)) 392 | } 393 | 394 | func main() { 395 | avg := average(10,20,30,40,50) 396 | println("Average:", avg) 397 | } 398 | ``` 399 | ## Type Function and Returning Functions 400 | 401 | 1. Functions can be assigned to variables `func0 := func() int {x++; return x}` as anonymous functions or to another declared functions 402 | 2. Functions that are returned from another functions has its own scope per returned function ( Closures yea ikr ? 🤷). 403 | ``` Go 404 | 405 | package main 406 | 407 | var x = 0 408 | 409 | func main() { 410 | //local x 411 | x := 0 412 | 413 | func0 := func() int {x++; return x} 414 | func1 := incrementGlobalX //without () 415 | func2 := wrapper() 416 | func3 := wrapper() 417 | 418 | println(func0(), " : func0 (local x)") 419 | println(func1(), " : func1 (global x)") 420 | println(func2(), " : func2 (per func scope x1)") 421 | println(func3(), " : func3 (per func scope x2)") 422 | println("Second Increment") 423 | println(func0(), " : func0 (local x)") 424 | println(func1(), " : func1 (global x)") 425 | println(func2(), " : func2 (per func scope x1)") 426 | println(func3(), " : func3 (per func scope x2)") 427 | } 428 | 429 | func incrementGlobalX() int { 430 | x++ 431 | return x 432 | } 433 | 434 | func wrapper() func() int { 435 | x := 0 436 | return func() int { 437 | x++ 438 | return x 439 | } 440 | } 441 | ``` 442 | 443 | 444 | ## Callbacks - Passing functions as argument 445 | ```Go 446 | func visit(numbers []int, callback func(int2 int)){ 447 | for _, n := range numbers { 448 | callback(n*2) 449 | } 450 | } 451 | func main() { 452 | visit([]int{1,2,3,4}, func(n int){ 453 | fmt.Println(n, "- Printed withing the callback function.") 454 | }) 455 | } 456 | ``` 457 | ## Defer Keyword 458 | 459 | `Defer` used before a functions executes the function at the end of the scope of it, think of it as a destructor for the scope. usually used to close opened files/buffers so u open the file and closes it using defer in the next line to keep things clean. they're executed as a **stack**. 460 | ```Go 461 | fmt.Println("One") 462 | defer fmt.Println("Four") 463 | defer fmt.Println("Three") 464 | fmt.Println("Two") 465 | 466 | //Prints One Two Three Four 467 | ``` 468 | ## Receivers 469 | 470 | Receiver are the way you create a method for a specific type/struct 471 | ``` Go 472 | type rect struct { 473 | width, height int 474 | } 475 | 476 | func (r *rect) area() int { 477 | return r.width * r.height 478 | } 479 | 480 | //used as 481 | r := rect{2,3} 482 | areaX := r.area() 483 | fmt.Println(areaX) 484 | ``` 485 | ## Overriding Receivers 486 | ```Go 487 | type Person struct { 488 | First string 489 | Last string 490 | Age int 491 | } 492 | 493 | type Employee struct { 494 | Person 495 | ID string 496 | Salary int 497 | } 498 | 499 | func (p Person) FullName() string{ 500 | return p.First + " " + p.Last 501 | } 502 | 503 | //Override 504 | func (p Employee) FullName() string{ 505 | return p.ID + " " + p.First + " " + p.Last 506 | } 507 | 508 | 509 | func main() { 510 | x := Employee{ 511 | Person{ 512 | "Sherif", 513 | "Abdel-Naby", 514 | 12}, 515 | "0ID12000ID", 516 | 9999, 517 | } 518 | 519 | fmt.Println(x) 520 | fmt.Println(x.Person.FullName()) //Sherif Abdel-Naby 521 | fmt.Println(x.FullName()) //0ID12000ID Sherif Abdel-Naby 522 | ``` 523 | 524 | 525 | -------------------------------------------------------------------------- 526 | 527 | 528 | 529 | # 🏗Go Data-Structures 530 | 531 | Arrays, Slices, Maps, and Structs 532 | 533 | ## Arrays 534 | 535 | - Arrays are of **static size**, size can't be changed in arrays. 536 | - Arrays elements do not need to be initialized explicitly; the zero value of the type is the initial value. 537 | ``` Go 538 | var x[15] int 539 | var twoD [2][3] int 540 | ``` 541 | ## Slices 542 | 543 | Slices are of dynamic size. 544 | ``` GO 545 | letters := []string{"a", "b", "c", "d"} 546 | 547 | /* using make -> make([]T, len, cap) */ 548 | var s []byte 549 | s = make([]byte, 5, 5) 550 | //OR 551 | s := make([]byte, 5) 552 | 553 | // both equiavlent to: s == []byte{0, 0, 0, 0, 0} 554 | ``` 555 | - A slice does not store any data, it just describes a section of an underlying array. Changing the elements of a slice modifies the corresponding elements of its underlying array. **Other slices that share the same underlying array will see those changes**. 556 | - Slicing a slice changes pointers of the underlying array, so it is as efficient as manipulating array indices, size and capacity of the new slice are changed too, capacity is equal `old capacity - sliced part from the beginning only` 557 | ```gO 558 | names := [4]string{"John","Paul","George","Ringo",} 559 | fmt.Println(names) //[John Paul George Ringo] 560 | a := names[0:2] 561 | b := names[1:3] 562 | fmt.Println(a, b) //[John Paul] [Paul George] 563 | b[0] = "XXX" 564 | fmt.Println(a, b) //[John XXX] [XXX George] 565 | fmt.Println(names) //[John XXX George Ringo] 566 | 567 | //ALSO 568 | 569 | //This is an array literal: 570 | [3]bool{true, true, false} 571 | 572 | //And this creates the same array as above, then builds a slice that references it: 573 | []bool{true, true, false} 574 | ``` 575 | ## Iterating over a Slice 576 | ``` Go 577 | for i, v := range arr { //do stuff } 578 | for _, v := range arr { //do stuff } 579 | for i, _ := range arr { //do stuff } 580 | ``` 581 | 582 | ## Appending to Slice 583 | 584 | Append return a whole new array (not reference). 585 | ``` Go 586 | var s []int 587 | // append works on nil slices. 588 | s = append(s, 0) 589 | // The slice grows as needed. 590 | s = append(s, 1) 591 | ``` 592 | Append add element at the end of the slice **if there is enough capacity** and r**eturn a reference type**!, if **not enough capacity** it allocate and copy to a new array and **return it as a new value**! and the **old array points to the old data**. 593 | 594 | If Append had enough capacity (didn't allocate new array) then ***changing a value in the new returned array changes the value in the old***! but if it allocated a new array to expand capacity, then **changing a value at an index of the newly returned array *DOESN'T* change the old array!** 595 | 596 | consider only using append where the left hand side is the same variable in the append first argument **(S = append(S, .....) )** to avoid any unexpected results 597 | 598 | ``` Go 599 | //Allocate new capacity 600 | var s []int 601 | s = make([]int, 5, 5) 602 | x := append(s, 1, 2 ,3) 603 | x[0] = 1337 604 | s[0] = 6800 605 | fmt.Println(s,x) //[6800 0 0 0 0] [1337 0 0 0 0 1 2 3] 606 | 607 | //Doesn't allocat new capacity and return reference 608 | var s []int 609 | s = make([]int, 5, 150) 610 | x := append(s, 1, 2 ,3) 611 | x[0] = 1337 612 | s[0] = 6800 613 | fmt.Println(s,x) //[6800 0 0 0 0] [6800 0 0 0 0 1 2 3] 614 | //notice that 1337 is overwritten 615 | ``` 616 | ## Common Slice Functions 617 | 618 | **Append Another Slice** 619 | ``` Go 620 | a = append(a, b...) 621 | ``` 622 | **Copy** 623 | 624 | Copy only copy elements of size = min(len(a), len(b)). so, the new slice to which a copy is to be made must have a size == size of the original array to have all elements copied. 625 | ```Go 626 | b = make([]T, len(a)) 627 | copy(b, a) 628 | // or 629 | b = append([]T(nil), a...) 630 | ``` 631 | **Cut** 632 | ```Go 633 | a = append(a[:i], a[j:]...) 634 | ``` 635 | **Delete** 636 | ```Go 637 | a = append(a[:i], a[i+1:]...) 638 | // or 639 | a = a[:i+copy(a[i:], a[i+1:])] 640 | ``` 641 | ## Slices Tricks 642 | 643 | [golang/go](https://github.com/golang/go/wiki/SliceTricks) 644 | 645 | ## Maps 646 | 647 | From [https://play.golang.org/p/U67R66Oab8r](https://play.golang.org/p/U67R66Oab8r) 648 | ```Go 649 | // To create an empty map, use the builtin `make`: 650 | // `make(map[key-type]val-type)`. 651 | m := make(map[string]int) 652 | 653 | // Set key/value pairs using typical `name[key] = val` 654 | // syntax. 655 | m["k1"] = 7 656 | m["k2"] = 13 657 | 658 | // Printing a map with e.g. `fmt.Println` will show all of 659 | // its key/value pairs. 660 | fmt.Println("map:", m) 661 | 662 | // Get a value for a key with `name[key]`. 663 | v1 := m["k1"] 664 | fmt.Println("v1: ", v1) 665 | 666 | // The builtin `len` returns the number of key/value 667 | // pairs when called on a map. 668 | fmt.Println("len:", len(m)) 669 | 670 | // The builtin `delete` removes key/value pairs from 671 | // a map. 672 | delete(m, "k2") 673 | fmt.Println("map:", m) 674 | 675 | // The optional second return value when getting a 676 | // value from a map indicates if the key was present 677 | // in the map. This can be used to disambiguate 678 | // between missing keys and keys with zero values 679 | // like `0` or `""`. Here we didn't need the value 680 | // itself, so we ignored it with the _blank identifier_ 681 | // `_`. 682 | _, prs := m["k2"] 683 | fmt.Println("prs:", prs) 684 | 685 | //You can use zero value to check if value exist. use second return instead. 686 | fmt.Println("key not found (gets zero value):", m["notFoundKey"]) 687 | 688 | 689 | // You can also declare and initialize a new map in 690 | // the same line with this syntax. 691 | n := map[string]int{"foo": 1, "bar": 2} 692 | fmt.Println("map:", n) 693 | ``` 694 | 695 | 696 | -------------------------------------------------------------------------- 697 | 698 | 699 | 700 | # 🏢Go Structs / OOP 701 | 702 | ## Notes 703 | 704 | - in Go you don't create classes, but create types 705 | - in Go you don't do inheritance, you create a value of the type (sort of delegation or embedding a type inside the other and use its methods with some syntactic sugar. 706 | - in Go structs fields can have a tag, it is written between `' '` after field type, tags can be used to e.g exclude or rename a field when Encoding/Decoding it to/from JSON 707 | - Fields and Methods in GO that start with **Uppercase** are exported, hence **they are not seen outside the package and in another packages**, lowercase are unexported fields which are only seen inside their package. e,g Json Encode won't encode unexported fields as Json Encoder package won't be able to access it. 708 | 709 | ## Go supports 710 | 711 | ### Encapsulation 712 | 713 | - State/Fields 714 | - Methods 715 | - Public/Private → Exported/unexported 716 | 717 | ### Inheritance and Reusability 718 | 719 | ``` Go 720 | 721 | package main 722 | import "fmt" 723 | 724 | type Parent struct { 725 | First string 726 | Last string 727 | Age int 728 | } 729 | 730 | type Child struct { 731 | Parent 732 | First string 733 | Middle string 734 | } 735 | 736 | 737 | func main() { 738 | 739 | x := Child{ 740 | Parent{ 741 | "First", 742 | "Last", 743 | 12}, 744 | "Child's First", 745 | "Middle", 746 | } 747 | 748 | fmt.Println(x) 749 | 750 | fmt.Println(x.First) 751 | fmt.Println(x.Parent.First) 752 | fmt.Println(x.Middle) 753 | 754 | fmt.Println(x.Last) 755 | fmt.Println(x.Parent.Last) 756 | 757 | fmt.Println(x.Age) 758 | fmt.Println(x.Parent.Age) 759 | 760 | } 761 | ``` 762 | Packages might need your type to implement its interface to work, for example the `sort` package requires you to implement `swap`, `less`, `equal` methods in order to work. also `fmt.Println()` requires you to implement `func (t T) String() string` 763 | 764 | ### Polymorphism and Interfaces 765 | 766 | A type implements an interface by implementing its methods. There is no explicit declaration of intent, no "implements" keyword. 767 | 768 | ``` Go 769 | 770 | // Here's a basic interface for geometric shapes. 771 | type geometry interface { 772 | area() float64 773 | perim() float64 774 | //extraFunc() string //if we uncomment this, so rect and circle won't 775 | // be implementing the geometry interface 776 | } 777 | 778 | // For our example we'll implement this interface on 779 | // `rect` and `circle` types. 780 | type rect struct { 781 | width, height float64 782 | 783 | } 784 | type circle struct { 785 | radius float64 786 | } 787 | 788 | // To implement an interface in Go, we just need to 789 | // implement all the methods in the interface. Here we 790 | // implement `geometry` on `rect`s. 791 | func (r rect) area() float64 { 792 | return r.width * r.height 793 | } 794 | func (r rect) perim() float64 { 795 | return 2*r.width + 2*r.height 796 | } 797 | 798 | // The implementation for `circle`s. 799 | func (c circle) area() float64 { 800 | return math.Pi * c.radius * c.radius 801 | } 802 | func (c circle) perim() float64 { 803 | return 2 * math.Pi * c.radius 804 | } 805 | 806 | // If a variable has an interface type, then we can call 807 | // methods that are in the named interface. Here's a 808 | // generic `measure` function taking advantage of this 809 | // to work on any `geometry`. 810 | func measure(g geometry) { 811 | fmt.Println(g) 812 | fmt.Println(g.area()) 813 | fmt.Println(g.perim()) 814 | } 815 | 816 | func main() { 817 | r := rect{width: 3, height: 4} 818 | c := circle{radius: 5} 819 | 820 | // The `circle` and `rect` struct types both 821 | // implement the `geometry` interface so we can use 822 | // instances of 823 | // these structs as arguments to `measure`. 824 | measure(r) 825 | measure(c) 826 | } 827 | ``` 828 | ### Overriding 829 | 830 | ``` Go 831 | 832 | type Person struct { 833 | First string 834 | Last string 835 | Age int 836 | } 837 | 838 | type Employee struct { 839 | Person 840 | ID string 841 | Salary int 842 | } 843 | 844 | func (p Person) FullName() string{ 845 | return p.First + " " + p.Last 846 | } 847 | 848 | //Override 849 | func (p Employee) FullName() string{ 850 | return p.ID + " " + p.First + " " + p.Last 851 | } 852 | 853 | 854 | func main() { 855 | 856 | x := Employee{ 857 | Person{ 858 | "Sherif", 859 | "Abdel-Naby", 860 | 12}, 861 | "0ID12000ID", 862 | 9999, 863 | } 864 | 865 | fmt.Println(x) 866 | fmt.Println(x.Person.FullName()) //Sherif Abdel-Naby 867 | fmt.Println(x.FullName()) //0ID12000ID Sherif Abdel-Naby 868 | ``` 869 | 870 | 871 | -------------------------------------------------------------------------- 872 | 873 | 874 | 875 | # 🥂Go Concurrency 876 | 877 | ## Intro 878 | 879 | Go Concurrency is made available by what's called `go-routines` , basically when a function is preceded with the `go` keyword, it runs in a `go-routine`, think of go-routine as a thread (***though they're different...**)**.*** go-routines is one of the most important features of Go that makes it and its concurrency model special. 880 | 881 | For data synchronization you can use mutex Locks, WaitGroups, and Atomic operations, however.. 882 | **It's recommended** to use Go Channels for data synchronization, though using the sync package (using mutex, locks, atomics, and WaitGroups) is also usable if it make sense for your use case. 883 | 884 | --- 885 | 886 | ## Notes 887 | 888 | 1. GO executable exits with active go routines running. 889 | 890 | ## mutex Locks, WaitGroups, and Atomic operations 891 | 892 | [//TODO](//todo) Example on using synchronization by mutex Locks, WaitGroups, and Atomic operations 893 | 894 | ## Go Channels 895 | 896 | - channels in layman terms are like a synchronized bucket that contains data, a go-routine can add data to the channel, or extract data from the channel. There are unbuffered channels, and buffered channels. for unbuffered channels if you're adding data to the channel, adding another data will be blocking until another go-routine extract such data. on the other hand receiving is also blocking until data is put in the channel. 897 | GO Buffered channel add a buffer to the go channel to avoid stalls, however it is not recommended to use it as a beginner, uses it only when it makes sense. 898 | - Channels can be `bidirectional (chan)`, `receive (<-chan)` only, or `send only(chan <-)` , send/receive only channels are useful when channels are passed as arguments, this indicates(and rather enforces) that the passed channel can only be received from (and you can send to), so this introduces some sort of control over how channels are used. think of pkgs where I don't want users to send anything to my channel. 899 | 900 | ### Example 1 901 | 902 | Note that I am using time.Sleep at the end to wait for the code to execute as the program will instantly close after running the two go routines. 903 | ```Go 904 | c := make(chan int) 905 | 906 | go func() { 907 | for i := 0; i < 9; i++ { 908 | time.Sleep(time.Second) 909 | c <- i 910 | } 911 | }() 912 | 913 | go func() { 914 | for{ 915 | fmt.Println( <- c ) 916 | } 917 | }() 918 | 919 | time.Sleep(time.Second * 15) 920 | ``` 921 | ### Example 2 922 | 923 | Using Range on a channel, it will iterate over values added on the channel until the channel is closed. 924 | No need to use time.sleep as the for-range is a blocking function. 925 | ```Go 926 | c := make(chan int) 927 | 928 | go func() { 929 | for i := 0; i <= 10; i++ { 930 | time.Sleep(time.Second) 931 | c <- i 932 | } 933 | close(c) 934 | }() 935 | 936 | for n := range c{ 937 | fmt.Println(n) 938 | } 939 | ``` 940 | ### Example 3 941 | 942 | Using wait-group to use more than 1 function to write to the same channel. 943 | Using a `waitGroup` to close the channel once the two writing functions signal `wg.Done()` 944 | ```Go 945 | c := make(chan int) 946 | 947 | var wg sync.WaitGroup 948 | 949 | wg.Add(2) 950 | 951 | go func() { 952 | for i := 0; i <= 10; i++ { 953 | time.Sleep(time.Millisecond * 350) 954 | c <- i 955 | } 956 | wg.Done() 957 | }() 958 | 959 | go func() { 960 | for i := 1000; i <= 1010; i++ { 961 | time.Sleep(time.Millisecond * 350) 962 | c <- i 963 | } 964 | wg.Done() 965 | }() 966 | 967 | go func() { 968 | wg.Wait() 969 | close(c) 970 | }() 971 | 972 | for n := range c{ 973 | fmt.Println(n) 974 | } 975 | ``` 976 | ### Example 4 977 | 978 | Using dynamic number of function calls. 979 | 980 | Also notice passing i inside the go func, this is because the value outside is in a for-loop, hence it is changing, so using it inside the the go-routine will lead to unexpected results. 981 | ```Go 982 | c := make(chan string) 983 | 984 | var wg sync.WaitGroup 985 | 986 | n := 10 987 | 988 | wg.Add(n) 989 | 990 | for i := 0; i < n; i++ { 991 | go func(i int) { 992 | for t := i*10; t < i*10 + 10; t++ { 993 | c <- "From " + strconv.Itoa(i) + " : " + strconv.Itoa(t) 994 | } 995 | wg.Done() 996 | }(i) 997 | } 998 | 999 | go func() { 1000 | wg.Wait() 1001 | close(c) 1002 | }() 1003 | 1004 | for x := range c{ 1005 | fmt.Println(x) 1006 | } 1007 | ``` 1008 | ### Example 5 - Semaphores 1009 | 1010 | Using only channels without waitGroup. 1011 | This is done using a channel that store bool (or anything really), and use a function to receive n-done signals then close the main channel. 1012 | ```Go 1013 | c := make(chan string) 1014 | done := make(chan bool) 1015 | 1016 | n := 2 1017 | 1018 | 1019 | for i := 0; i < n; i++ { 1020 | go func(i int) { 1021 | for t := i*10; t < i*10 + 10; t++ { 1022 | c <- "From " + strconv.Itoa(i) + " : " + strconv.Itoa(t) 1023 | } 1024 | done <- true 1025 | }(i) 1026 | } 1027 | 1028 | go func() { 1029 | //receive the n-dones from the go-routines 1030 | for i := 0; i < n; i++{ 1031 | <- done 1032 | } 1033 | close(c) 1034 | }() 1035 | 1036 | for x := range c{ 1037 | fmt.Println(x) 1038 | } 1039 | ``` 1040 | 1041 | ### Example 6 - Using channels as arguments/returns 1042 | 1043 | In this example we sum values from 0 to i. e.g( i = 3 → 0+1+2+3 = 6) 1044 | 1045 | --- 1046 | 1047 | This code we create a go routine that feeds the **increment channels values** 1,2,3 1048 | another Channel called the **sum channel** will take the **increment channel and processes its values ( so the sum channel will run until the increment channel closes )**, then the sum channel will **put its sum value for the main to pick**. the point here that main can do other stuff while sum channel finish processing. also we can pass any type of channel to sum channel to sum not necessary an incrementing values in a decoupled way. 1049 | ```Go 1050 | func main() { 1051 | 1052 | i := 10 1053 | 1054 | //Return A Channel that produces 1, 2, 3...n 1055 | c := incrementer(i) 1056 | 1057 | // Take a channel that produces 1,2,3...n and sum these numbers 1058 | // returns a channel that have the data in it after summation (so it is not blocking the main itself) 1059 | cSum := puller(c) 1060 | 1061 | /* DO STUFF WHILE Puller is working (that's why it is returning a channel */ 1062 | 1063 | //Pull from the puller when we want the result (This is blocking now) 1064 | //Result for i := 10 should be : 10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 = 55 1065 | fmt.Println("Final Sum", <- cSum) 1066 | 1067 | } 1068 | 1069 | //returns an ACTIVE go routine that produces 1,2,3..n 1070 | func incrementer(n int) chan int { 1071 | out := make(chan int, 10) 1072 | 1073 | //no need to pass n as parameter as it is a non-changing variable in this context 1074 | go func() { 1075 | for i := 0; i <= n; i++ { 1076 | fmt.Println("From incrementer: Produced i = ", i ) 1077 | out <- i 1078 | //just to illustrate it is blocking in main. 1079 | time.Sleep(time.Millisecond * 100) 1080 | } 1081 | close(out) 1082 | }() 1083 | return out 1084 | } 1085 | 1086 | //takes a channel that produces numbers that are to be summed together. 1087 | func puller(c chan int) chan int { 1088 | out := make (chan int) 1089 | go func() { 1090 | var sum int 1091 | for n := range c{ 1092 | fmt.Println("From Puller go-routine: Sum + i ->", sum, "+", n, "=", sum + n) 1093 | sum += n 1094 | } 1095 | fmt.Println("Summation Finished -> Outputing SUM") 1096 | out <- sum 1097 | // also we can output each sum stage for whoever uses the channel and close when finish. 1098 | //close(out) 1099 | }() 1100 | return out 1101 | } 1102 | ``` 1103 | ``` 1104 | Output: 1105 | 1106 | From incrementer: Produced i = 0 1107 | From Puller go-routine: Sum + i -> 0 + 0 = 0 1108 | From incrementer: Produced i = 1 1109 | From Puller go-routine: Sum + i -> 0 + 1 = 1 1110 | From incrementer: Produced i = 2 1111 | From Puller go-routine: Sum + i -> 1 + 2 = 3 1112 | From incrementer: Produced i = 3 1113 | From Puller go-routine: Sum + i -> 3 + 3 = 6 1114 | From incrementer: Produced i = 4 1115 | From Puller go-routine: Sum + i -> 6 + 4 = 10 1116 | From incrementer: Produced i = 5 1117 | From Puller go-routine: Sum + i -> 10 + 5 = 15 1118 | Summation Finished -> Outputing SUM 1119 | Final Sum 15 1120 | ``` 1121 | 1122 | 1123 | -------------------------------------------------------------------------- 1124 | 1125 | 1126 | 1127 | # 🐞Go Error Handling 1128 | 1129 | ## Notes 1130 | Go doesn't use try/catch and exceptions to handle errors, instead, functions also returns an error along with its own return. programmer should then check this error by an if-condition and deiced what to do accordingly 1131 | 1132 | ## Example 1133 | 1134 | from "Go by Example"... 1135 | ```Go 1136 | // In Go it's idiomatic to communicate errors via an 1137 | // explicit, separate return value. This contrasts with 1138 | // the exceptions used in languages like Java and Ruby and 1139 | // the overloaded single result / error value sometimes 1140 | // used in C. Go's approach makes it easy to see which 1141 | // functions return errors and to handle them using the 1142 | // same language constructs employed for any other, 1143 | // non-error tasks. 1144 | 1145 | package main 1146 | 1147 | import "errors" 1148 | import "fmt" 1149 | 1150 | // By convention, errors are the last return value and 1151 | // have type `error`, a built-in interface. 1152 | func f1(arg int) (int, error) { 1153 | if arg == 42 { 1154 | 1155 | // `errors.New` constructs a basic `error` value 1156 | // with the given error message. 1157 | return -1, errors.New("can't work with 42") 1158 | 1159 | } 1160 | 1161 | // A `nil` value in the error position indicates that 1162 | // there was no error. 1163 | return arg + 3, nil 1164 | } 1165 | 1166 | // It's possible to use custom types as `error`s by 1167 | // implementing the `Error()` method on them. Here's a 1168 | // variant on the example above that uses a custom type 1169 | // to explicitly represent an argument error. 1170 | type argError struct { 1171 | arg int 1172 | prob string 1173 | } 1174 | 1175 | func (e *argError) Error() string { 1176 | return fmt.Sprintf("%d - %s", e.arg, e.prob) 1177 | } 1178 | 1179 | func f2(arg int) (int, error) { 1180 | if arg == 42 { 1181 | 1182 | // In this case we use `&argError` syntax to build 1183 | // a new struct, supplying values for the two 1184 | // fields `arg` and `prob`. 1185 | return -1, &argError{arg, "can't work with it"} 1186 | } 1187 | return arg + 3, nil 1188 | } 1189 | 1190 | func main() { 1191 | 1192 | // The two loops below test out each of our 1193 | // error-returning functions. Note that the use of an 1194 | // inline error check on the `if` line is a common 1195 | // idiom in Go code. 1196 | for _, i := range []int{7, 42} { 1197 | if r, e := f1(i); e != nil { 1198 | fmt.Println("f1 failed:", e) 1199 | } else { 1200 | fmt.Println("f1 worked:", r) 1201 | } 1202 | } 1203 | for _, i := range []int{7, 42} { 1204 | if r, e := f2(i); e != nil { 1205 | fmt.Println("f2 failed:", e) 1206 | } else { 1207 | fmt.Println("f2 worked:", r) 1208 | } 1209 | } 1210 | 1211 | // If you want to programmatically use the data in 1212 | // a custom error, you'll need to get the error as an 1213 | // instance of the custom error type via type 1214 | // assertion. 1215 | _, e := f2(42) 1216 | if ae, ok := e.(*argError); ok { 1217 | fmt.Println(ae.arg) 1218 | fmt.Println(ae.prob) 1219 | } 1220 | ``` 1221 | -------------------------------------------------------------------------------- /simple-hello-world.go: -------------------------------------------------------------------------------- 1 | package main 2 | import "fmt" 3 | func main() { 4 | printHelloWorld("Sherif") 5 | } 6 | 7 | 8 | func printHelloWorld(name string){ 9 | fmt.Printf("Hello World, %s!", name) 10 | } 11 | --------------------------------------------------------------------------------