├── .gitignore ├── 01-hello-world ├── README.md ├── go.mod └── helloworld.go ├── 02-variables ├── README.md ├── go.mod └── variables.go ├── 03-stdin-and-comma-idioms ├── README.md ├── go.mod └── stdin-and-comma-idioms.go ├── 04-type-conversion-error-time-handling ├── README.md ├── example.go └── go.mod ├── 05-build-internals-memory-management ├── README.md ├── example ├── example.go └── go.mod ├── 06-pointers ├── README.md ├── example.go └── go.mod ├── 07-arrays-slices-structs-map ├── README.md ├── example.go └── go.mod ├── 08-make-new-other-keywords ├── README.md ├── example.go └── go.mod ├── 09-if-else-loops-switch ├── README.md ├── example.go └── go.mod ├── 10-functions-methods ├── README.md ├── example.go └── go.mod └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | 20 | # Go workspace file 21 | go.work 22 | -------------------------------------------------------------------------------- /01-hello-world/README.md: -------------------------------------------------------------------------------- 1 | ## Go "Hello World" Program Explained 2 | 3 | Welcome to this tutorial where we'll explore the classic "Hello World" program in Go, line by line. This program is a fundamental starting point in learning any programming language and offers a simple example of Go's syntax and structure. 4 | 5 | ### The Program 6 | 7 | ```go 8 | package main 9 | 10 | import "fmt" 11 | 12 | func main() { 13 | fmt.Println("Hello World!") 14 | } 15 | ``` 16 | 17 | ### Breakdown 18 | 19 | 1. **`package main`** 20 | 21 | This is the package declaration. Every Go program starts with a package declaration. Packages are Go's way of organizing and reusing code. The `main` package is special. It defines a standalone executable program, not a library. When the Go runtime starts, it looks for the `main` package and runs it. 22 | 23 | 2. **`import "fmt"`** 24 | 25 | This line tells the Go compiler to include the `fmt` package. The `fmt` package (short for format) is part of the Go standard library and contains functions for formatting text, including printing to the console. This package is necessary for outputting information like "Hello, World!". 26 | 27 | 3. **`func main() { ... }`** 28 | 29 | - `func` is a keyword in Go used to declare a function. 30 | - `main` is the name of the function. `main` is a special name indicating the entry point of the program. When the program runs, it starts by executing the code in the `main` function. 31 | - The curly braces `{ }` define the start and end of the function’s code block. 32 | 33 | 4. **`fmt.Println("Hello, World!")`** 34 | 35 | - This line is the actual code executed by the `main` function. 36 | - `fmt.Println` is a function from the `fmt` package that prints its arguments to the standard output (in most cases, this is the terminal/console). 37 | - The string `"Hello, World!"` is passed as an argument to `fmt.Println`, which then outputs it. 38 | - The `Println` function automatically adds a newline character after the output, so the next output starts on a new line. 39 | 40 | 41 | -------------------------------------------------------------------------------- /01-hello-world/go.mod: -------------------------------------------------------------------------------- 1 | module helloworld 2 | 3 | go 1.21.5 4 | -------------------------------------------------------------------------------- /01-hello-world/helloworld.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Println("Hello world!") 7 | } 8 | -------------------------------------------------------------------------------- /02-variables/README.md: -------------------------------------------------------------------------------- 1 | ## What is a Variable? 2 | 3 | A variable in Go is a named storage location that holds a value of a specific type. Variables allow you to store and manipulate data in your programs. 4 | 5 | ## Declaring Variables 6 | 7 | There are several ways to declare variables in Go: 8 | 9 | 1. **Standard Declaration**: 10 | - Syntax: `var variableName variableType` 11 | - Example: `var age int` 12 | 13 | 2. **Declaration with Initialization**: 14 | - Syntax: `var variableName variableType = value` 15 | - Example: `var age int = 30` 16 | 17 | 3. **Short Variable Declaration (Type Inference)**: 18 | - Syntax: `variableName := value` 19 | - Note: Type is inferred by the compiler. 20 | - Example: `age := 30` (used within functions) 21 | 22 | ## Variable Scope and Visibility 23 | 24 | - **Local Scope**: Variables declared within a function are local to that function. 25 | - **Package Scope**: Variables declared outside of a function but within a package are accessible throughout the package. 26 | - **Exported vs. Unexported**: 27 | - Variables with names starting with an uppercase letter are exported and accessible from other packages. 28 | - Variables starting with a lowercase letter are unexported and only accessible within their package. 29 | 30 | 1. **Exported Identifiers:** 31 | - If a variable, constant, function, type, etc., starts with an uppercase letter, it is considered exported. 32 | - Exported identifiers are visible and accessible from other packages. 33 | - This is analogous to public variables or methods in some other programming languages. 34 | - Example: `AppName`, `PrintDetails()` 35 | 36 | 2. **Unexported Identifiers:** 37 | - Identifiers that start with a lowercase letter are unexported. 38 | - They are only accessible within the package where they are declared. 39 | - This is similar to private variables or methods in other languages. 40 | - Example: `appVersion`, `calculateResult()` 41 | 42 | 1. **Main Package File (`main.go`):** 43 | ```go 44 | package main 45 | 46 | import ( 47 | "fmt" 48 | "example/util" // assuming the utility functions are in "example/util" package 49 | ) 50 | 51 | // Exported variable 52 | var AppName = "GoScopeApp" 53 | 54 | // Unexported variable 55 | var appVersion = "1.0.0" 56 | 57 | func main() { 58 | fmt.Println("Welcome to", AppName) 59 | fmt.Println("Version:", appVersion) 60 | 61 | // Accessing an exported function from the util package 62 | util.PrintAppDetails(AppName, appVersion) 63 | } 64 | ``` 65 | 66 | 2. **Utility Package File (`util/util.go`):** 67 | - Assume this file is in a separate package named `util`. 68 | ```go 69 | package util 70 | 71 | import "fmt" 72 | 73 | // Exported function 74 | func PrintAppDetails(name, version string) { 75 | fmt.Println("Application:", name) 76 | fmt.Println("Version:", version) 77 | } 78 | ``` 79 | 80 | In this example: 81 | - `AppName` in `main.go` is exported (uppercase) and is accessible from other packages, including the `util` package. 82 | - `appVersion` in `main.go` is unexported (lowercase) and is not accessible from the `util` package or any other package. It's only accessible within the `main` package. 83 | - The `PrintAppDetails` function in `util.go` is an exported function, making it accessible in the `main` package. 84 | 85 | ## Variable Types in Go 86 | 87 | Go supports a variety of variable types: 88 | 89 | 1. **Basic Types**: 90 | - Integers: `int`, `int8`, `int16`, `int32`, `int64` 91 | - Unsigned Integers: `uint`, `uint8`, `uint16`, `uint32`, `uint64` 92 | - Floating Point: `float32`, `float64` 93 | - Complex Numbers: `complex64`, `complex128` 94 | - Booleans: `bool` 95 | - Strings: `string` 96 | 97 | 2. **Composite Types**: 98 | - Arrays 99 | - Slices 100 | - Structs 101 | - Maps 102 | - Pointers 103 | - Functions 104 | - Channels 105 | 106 | 3. **Interface Type**: Used to specify methods that a type must implement. 107 | 108 | ## Best Practices for Using Variables 109 | 110 | - **Use Descriptive Names**: Choose names that clearly describe what the variable represents. 111 | - **Short and Concise**: Prefer shorter names for local variables within a small scope and longer, descriptive names for global variables or variables with a larger scope. 112 | - **CamelCase Naming**: Use `camelCase` for variable names (e.g., `totalAmount`, `userName`). 113 | - **Minimize Scope**: Declare variables close to where they are used to reduce the scope and improve readability. 114 | - **Immutable vs. Mutable**: 115 | - In Go, variables are mutable by default; their values can be changed. 116 | - For immutable "variables", use constants (`const`) which cannot be reassigned after declaration. 117 | - Example: 118 | ```go 119 | const pi = 3.14 120 | ``` 121 | 122 | ## Using Variables 123 | 124 | Variables in Go are used to store, modify, and retrieve data. You can perform operations on variables according to their types. 125 | 126 | Example: 127 | ```go 128 | var message string = "Hello, Go!" 129 | fmt.Println(message) // Output: Hello, Go! 130 | ``` 131 | 132 | 133 | -------------------------------------------------------------------------------- /02-variables/go.mod: -------------------------------------------------------------------------------- 1 | module variables 2 | 3 | go 1.21.5 4 | -------------------------------------------------------------------------------- /02-variables/variables.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/cmplx" 6 | ) 7 | 8 | func main() { 9 | // Integer Types 10 | // 'int' size is dependent on the system architecture (32-bit or 64-bit) 11 | var age int = 30 12 | var year uint = 2024 13 | 14 | // Floating Point Types 15 | // 'float32' and 'float64' are for floating-point numbers with 32 and 64 bits of precision respectively 16 | var temperature float32 = 26.5 17 | var distance float64 = 384400.0 // Distance to the Moon in km 18 | 19 | // Complex Numbers 20 | // 'complex128' is used for complex numbers with double precision 21 | var complexNumber complex128 = cmplx.Sqrt(-5 + 12i) 22 | 23 | // Boolean Type 24 | // 'bool' represents a boolean and is either true or false 25 | var isSunny bool = true 26 | 27 | // String Type 28 | // 'string' is a sequence of characters with immutable nature 29 | var greeting string = "Hello, Go!" 30 | 31 | // Composite Types 32 | // Array 33 | // Arrays have a fixed size defined at compile time 34 | var numbers [5]int = [5]int{1, 2, 3, 4, 5} 35 | 36 | // Slice 37 | // Slices are similar to arrays but their size is dynamic, they are a reference type 38 | var colors []string = []string{"Red", "Green", "Blue"} 39 | 40 | // Struct 41 | // Structs are custom types that group together variables of different types 42 | type Location struct { 43 | Latitude, Longitude float64 44 | } 45 | var officeLocation Location = Location{Latitude: 27.2046, Longitude: 77.4977} 46 | 47 | // Map 48 | // Maps are key-value pairs, they can grow dynamically and are reference types 49 | var capitals map[string]string = map[string]string{ 50 | "France": "Paris", 51 | "Japan": "Tokyo", 52 | "India": "New Delhi", 53 | } 54 | 55 | // Pointer 56 | // Pointers hold the memory address of a variable, allows for indirect manipulation of variable 57 | var pointerToAge *int = &age 58 | 59 | // Rune Type 60 | // 'rune' represents a Unicode character and is an alias for 'int32' 61 | var heart rune = '❤' 62 | 63 | // Print values of the variables 64 | fmt.Println("Integer:", age) 65 | fmt.Println("Unsigned Integer:", year) 66 | fmt.Println("Float32:", temperature) 67 | fmt.Println("Float64:", distance) 68 | fmt.Println("Complex Number:", complexNumber) 69 | fmt.Println("Boolean:", isSunny) 70 | fmt.Println("String:", greeting) 71 | fmt.Println("Array:", numbers, "\n Last Value:", numbers[len(numbers)-1]) 72 | fmt.Println("Slice:", colors) 73 | fmt.Println("Struct:", officeLocation, "\n Single value:", officeLocation.Latitude) 74 | fmt.Println("Map:", capitals, "\n Access the value:", capitals["France"]) 75 | fmt.Println("Pointer:", pointerToAge, "Pointing to value:", *pointerToAge) 76 | fmt.Println("Rune (Unicode Character):", string(heart), "\n Unicode value:", heart) 77 | } 78 | -------------------------------------------------------------------------------- /03-stdin-and-comma-idioms/README.md: -------------------------------------------------------------------------------- 1 | # Handling of Standard Input and Comma Idioms in Go 2 | 3 | ## Introduction 4 | 5 | This tutorial is about standard input (`stdin`) in Go and provides production-ready examples of the "comma ok" and "comma error" idioms. We'll also make a "guess the number" game to apply these concepts. 6 | 7 | ## Reading Standard Input in Go 8 | 9 | Reading input from the user in Go can be accomplished using functions like `fmt.Scanln()` and `bufio.Scanner`. 10 | 11 | ## Comma Ok Idiom - Production Example 12 | 13 | The "comma ok" idiom is essential for safely accessing values in maps and asserting interface types. Here's a production-ready example: 14 | 15 | ### Example: Accessing a Map Value 16 | 17 | ```go 18 | func getUserRole(roles map[string]string, username string) string { 19 | if role, ok := roles[username]; ok { 20 | return role // Username exists 21 | } 22 | return "guest" // Default role 23 | } 24 | ``` 25 | 26 | In this example, `roles` is a map with usernames as keys. The "comma ok" idiom checks if `username` is present in the map. It's a safe way to access map values without causing a panic. 27 | 28 | ## Comma Error Idiom - Production Example 29 | 30 | The "comma error" idiom is widely used for error handling. Here's how it might appear in a production setting: 31 | 32 | ### Example: Reading a File 33 | 34 | ```go 35 | func readFileContent(path string) (string, error) { 36 | content, err := ioutil.ReadFile(path) 37 | if err != nil { 38 | return "", err // Handle the error 39 | } 40 | return string(content), nil 41 | } 42 | ``` 43 | 44 | This function reads a file and returns its content. The "comma error" idiom is used to check if the `ReadFile` operation was successful. 45 | 46 | ## Custom Comma Ok Idiom 47 | 48 | The "comma ok" idiom can be extended to custom types, especially when dealing with type assertions and checking the presence of elements in complex data structures. 49 | 50 | ### Example: Custom Type Assertion 51 | 52 | ```go 53 | type Shape interface { 54 | Draw() string 55 | } 56 | 57 | type Circle struct {} 58 | 59 | func (c Circle) Draw() string { 60 | return "Drawing a circle" 61 | } 62 | 63 | // CustomCommaOk checks if the Shape is a Circle and returns a boolean accordingly 64 | func CustomCommaOk(shape Shape) (Circle, bool) { 65 | circle, ok := shape.(Circle) 66 | return circle, ok 67 | } 68 | ``` 69 | 70 | In this custom example, we're asserting whether a `Shape` interface is of type `Circle`. The function `CustomCommaOk` returns both the asserted type and a boolean indicating success. 71 | 72 | ## Custom Comma Error Idiom 73 | 74 | Similarly, the "comma error" idiom can be customized for functions that need to return a value and a custom error. 75 | 76 | ### Example: Custom Error Handling 77 | 78 | ```go 79 | type Result struct { 80 | Value string 81 | } 82 | 83 | // CustomCommaError simulates a function that returns a result and a custom error 84 | func CustomCommaError(condition bool) (Result, error) { 85 | if condition { 86 | return Result{Value: "Success"}, nil 87 | } else { 88 | return Result{}, fmt.Errorf("custom error occurred") 89 | } 90 | } 91 | ``` 92 | 93 | This function returns a `Result` and a custom error based on a condition. It's a pattern commonly used in Go to simultaneously handle return values and errors. 94 | 95 | 96 | -------------------------------------------------------------------------------- /03-stdin-and-comma-idioms/go.mod: -------------------------------------------------------------------------------- 1 | module user-input 2 | 3 | go 1.21.5 4 | -------------------------------------------------------------------------------- /03-stdin-and-comma-idioms/stdin-and-comma-idioms.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "math/rand" 7 | "os" 8 | "strconv" 9 | "time" 10 | ) 11 | 12 | func main() { 13 | rand.Seed(time.Now().UnixNano()) 14 | secretNumber := rand.Intn(100) + 1 15 | 16 | scanner := bufio.NewScanner(os.Stdin) 17 | 18 | for { 19 | fmt.Println("Enter the number") 20 | 21 | scanner.Scan() 22 | 23 | input := scanner.Text() 24 | 25 | guess, err := strconv.Atoi(input) 26 | if err != nil { 27 | fmt.Println("Please enter a valid input") 28 | continue 29 | } 30 | if guess < secretNumber { 31 | fmt.Println("Your number is lower!") 32 | } else if guess > secretNumber { 33 | fmt.Println("Your number higher!") 34 | } else { 35 | fmt.Println("Correct! :happy-face:") 36 | break 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /04-type-conversion-error-time-handling/README.md: -------------------------------------------------------------------------------- 1 | # Type Conversion, Error Handling, Time Management, and Panic/Recover 2 | 3 | ## Introduction 4 | 5 | This comprehensive tutorial covers type conversion, error handling, time management, and the use of panic/recover in Go, with practical examples for each. 6 | 7 | ## Type Conversion in Go 8 | 9 | ### Example: Integer to Float Conversion 10 | 11 | ```go 12 | var myInt int = 10 13 | var myFloat float64 = float64(myInt) 14 | fmt.Println(myFloat) // Output: 10.0 15 | ``` 16 | 17 | - **Caveat**: When converting from a larger to a smaller type, or from floating-point to integer, there's a risk of overflow or data loss. 18 | 19 | ## Comprehensive Error Handling in Go 20 | 21 | ### Example: Error Checking 22 | 23 | ```go 24 | file, err := os.Open("nonexistent.txt") 25 | if err != nil { 26 | log.Fatalf("Error opening file: %v", err) 27 | } 28 | defer file.Close() 29 | ``` 30 | 31 | - **Custom Error**: Creating custom errors using `fmt.Errorf` or by implementing the `Error()` method. 32 | 33 | ## Managing Time in Go 34 | 35 | ### Example: Formatting Time 36 | 37 | ```go 38 | currentTime := time.Now() 39 | formattedTime := currentTime.Format("2006-01-02 15:04:05") 40 | fmt.Println("Formatted Time:", formattedTime) 41 | ``` 42 | 43 | - **Time Arithmetic**: Adding and subtracting durations from `time.Time` objects. 44 | 45 | ## Panic and Recover in Go 46 | 47 | ### Example: Using Panic and Recover 48 | 49 | ```go 50 | func riskyFunction() { 51 | defer func() { 52 | if r := recover(); r != nil { 53 | fmt.Println("Recovered from:", r) 54 | } 55 | }() 56 | panic("something bad happened") 57 | } 58 | 59 | riskyFunction() 60 | ``` 61 | 62 | - **Best Practice**: Use `panic`/`recover` carefulluy, as a last resort, not as a substitute for regular error handling. 63 | 64 | 65 | -------------------------------------------------------------------------------- /04-type-conversion-error-time-handling/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | defer func() { 11 | if r := recover(); r != nil { 12 | fmt.Println("Recovered from error:", r) 13 | } 14 | }() 15 | 16 | fmt.Print("Enter the number of hours until the meeting: ") 17 | var input string 18 | fmt.Scanln(&input) 19 | 20 | hours, err := strconv.Atoi(input) 21 | if err != nil { 22 | fmt.Println("Error: Invalid input. Please enter a number.") 23 | return 24 | } 25 | 26 | if hours < 0 { 27 | panic("Error: Number of hours cannot be negative.") 28 | } 29 | 30 | meetingTime := time.Now().Add(time.Duration(hours) * time.Hour) 31 | fmt.Println("The meeting is scheduled for:", meetingTime.Format("2006-01-02 15:04:05")) 32 | } 33 | -------------------------------------------------------------------------------- /04-type-conversion-error-time-handling/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/siinghd/go-lang/04-type-conversion-error-time-handling 2 | 3 | go 1.21.5 4 | -------------------------------------------------------------------------------- /05-build-internals-memory-management/README.md: -------------------------------------------------------------------------------- 1 | ### Part 1: Building Go Applications 2 | 3 | #### 1. Understanding the Go Build Process 4 | 5 | **Compilation in Go** 6 | - Go compiles programs into binary executables. 7 | - Compilation involves converting Go source code into machine code. 8 | - Go's compiler aims for fast compilation times. 9 | 10 | **Linking** 11 | - After compilation, Go links the compiled packages into an executable. 12 | - Linking involves combining object files (.o files) and libraries. 13 | - Static linking (including all dependencies in the executable) is standard in Go. 14 | 15 | **Cross-Compilation** 16 | - Go supports cross-compilation, meaning you can compile a program for a platform different from the one you're using. 17 | - This is useful for creating binaries for multiple operating systems or architectures. 18 | - Set `GOOS` and `GOARCH` environment variables to target different platforms. 19 | 20 | #### 2. Build Tools and Commands 21 | 22 | **`go build`** 23 | - Compiles the packages named by the import paths. 24 | - Without arguments, it compiles the package in the current directory. 25 | - Does not install the binary, but places it in the current directory or specified location. 26 | 27 | **`go install`** 28 | - Compiles and installs the packages named by the import paths. 29 | - The binary is placed in the `$GOPATH/bin` or `$GOBIN` directory. 30 | 31 | **Flags and Options** 32 | - `-o`: Specifies the output file name. 33 | - `-v`: Verbose mode; prints the names of packages as they are compiled. 34 | - `-race`: Enables data race detection. 35 | 36 | #### 3. Optimizing Build Times 37 | 38 | **Dependency Management** 39 | - Efficient dependency management is key to faster build times. 40 | - Use modules to manage dependencies (introduced in Go 1.11). 41 | - Keep your dependencies updated and minimal. 42 | 43 | **Build Caching** 44 | - Go 1.10 introduced a build cache, automatically storing recent builds. 45 | - Reuses previously compiled packages if source files haven't changed. 46 | - Use the `-a` flag to force rebuilding of all packages. 47 | 48 | #### Example: Building a Simple Go Program 49 | 50 | ```go 51 | package main 52 | 53 | import "fmt" 54 | 55 | func main() { 56 | fmt.Println("Hello, Go!") 57 | } 58 | ``` 59 | 60 | - Build with `go build` to create an executable. 61 | - Use `go build -o hello` to specify the output binary name. 62 | - Cross-compile by setting `GOOS` and `GOARCH`, e.g., `GOOS=windows GOARCH=amd64 go build`. 63 | 64 | 65 | ### Part 2: Go Internals Deep Dive 66 | 67 | #### 1. Understanding Go's Runtime 68 | 69 | **Goroutines** 70 | - Goroutines are lightweight threads managed by the Go runtime. 71 | - They are cheaper to create and use less memory than traditional OS threads. 72 | - Go's runtime multiplexes goroutines onto a small number of OS threads. 73 | 74 | **Scheduler** 75 | - The scheduler manages the execution of goroutines. 76 | - It uses a technique called "m:n scheduling," balancing m goroutines over n OS threads. 77 | - The scheduler is non-preemptive by default but can preempt long-running goroutines since Go 1.14. 78 | 79 | #### 2. Garbage Collection 80 | 81 | **GC Mechanics** 82 | - Go uses a concurrent garbage collector. 83 | - The collector runs in parallel with the program, reducing pause times. 84 | - It's a mark-and-sweep garbage collector, tracing and freeing unused objects. 85 | 86 | **Optimizations** 87 | - Garbage collection can be tuned with `GOGC` environment variable. 88 | - Reduce allocation rates for more efficient garbage collection. 89 | - Use pooling (e.g., `sync.Pool`) for frequently allocated objects. 90 | 91 | #### 3. Interfaces and Reflection 92 | 93 | **Interface Internals** 94 | - Interfaces in Go are implemented using two values: a pointer to the type information and a pointer to the data. 95 | - This design allows for efficient and dynamic method dispatch. 96 | 97 | **Reflection** 98 | - Reflection in Go is provided by the `reflect` package. 99 | - It allows inspecting and manipulating objects at runtime, identifying their types, and calling methods dynamically. 100 | - Use reflection carefully, as it can complicate code and impact performance. 101 | 102 | #### Example: Working with Interfaces and Reflection 103 | 104 | ```go 105 | package main 106 | 107 | import ( 108 | "fmt" 109 | "reflect" 110 | ) 111 | 112 | type MyInterface interface { 113 | MyMethod() 114 | } 115 | 116 | type MyType struct {} 117 | 118 | func (m MyType) MyMethod() { 119 | fmt.Println("Method Implementation") 120 | } 121 | 122 | func main() { 123 | var myInterface MyInterface = MyType{} 124 | 125 | // Using reflection to examine the interface 126 | typeOfInterface := reflect.TypeOf(myInterface) 127 | fmt.Println("Type of interface:", typeOfInterface) 128 | 129 | // Invoking a method dynamically 130 | reflect.ValueOf(myInterface).MethodByName("MyMethod").Call(nil) 131 | } 132 | ``` 133 | 134 | ### Part 3: Memory Management in Go 135 | 136 | #### 1. Memory Allocation 137 | 138 | **Stack vs. Heap Allocation** 139 | - Go decides at compile time whether a variable will be allocated on the stack or heap. 140 | - Stack allocation is faster and more efficient but limited in size. 141 | - Heap allocation is used for variables that need to exist beyond the scope of their declaration (they "escape" to the heap). 142 | 143 | **Escape Analysis** 144 | - Go's compiler performs escape analysis to determine where to allocate memory. 145 | - It helps in optimizing memory usage by allocating variables on the stack when possible. 146 | - You can see escape analysis decisions using `go build -gcflags '-m'`. 147 | 148 | #### 2. Memory Efficiency 149 | 150 | **Reducing Memory Usage** 151 | - Reuse objects wherever possible to minimize allocation overhead. 152 | - Be mindful of the size of structs and arrays. 153 | - Use pointers to large structs to avoid unnecessary copying. 154 | 155 | **Profiling** 156 | - Go provides built-in tools for memory profiling (`runtime/pprof`). 157 | - Profiling helps identify areas of your code that are using excessive memory. 158 | - Use the `pprof` tool to analyze and visualize memory usage. 159 | 160 | #### 3. Advanced Topics 161 | 162 | **Pointers and Garbage Collection** 163 | - Pointers can keep objects on the heap longer than necessary, leading to increased memory usage. 164 | - Carefully manage pointer lifetimes and avoid circular references. 165 | 166 | **Concurrency and Memory** 167 | - Concurrency in Go can increase complexity in memory management. 168 | - Use channels or mutexes to safely share data between goroutines. 169 | - Be cautious of goroutine leaks, which can lead to memory leaks. 170 | 171 | #### Example: Memory Management and Profiling 172 | 173 | Here's a simple example to demonstrate memory allocation and profiling in Go: 174 | 175 | ```go 176 | package main 177 | 178 | import ( 179 | "fmt" 180 | "runtime" 181 | "time" 182 | ) 183 | 184 | func createLargeObject() *[]byte { 185 | a := make([]byte, 1024*1024) // Allocating 1MB 186 | return &a 187 | } 188 | 189 | func main() { 190 | // Allocating memory 191 | largeObject := createLargeObject() 192 | fmt.Println("Large object allocated") 193 | 194 | // Forcing garbage collection for demonstration purposes 195 | runtime.GC() 196 | fmt.Println("Garbage collection executed") 197 | 198 | // Preventing the program from exiting immediately 199 | time.Sleep(2 * time.Second) 200 | } 201 | ``` 202 | 203 | - Run this code with memory profiling enabled to observe the allocation and deallocation. 204 | 205 | 206 | -------------------------------------------------------------------------------- /05-build-internals-memory-management/example: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siinghd/go-lang/e8e2c317da791a2009de1283db6bf90a1dcfbf00/05-build-internals-memory-management/example -------------------------------------------------------------------------------- /05-build-internals-memory-management/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "time" 7 | ) 8 | 9 | // processData processes data received on the input channel and sends results on the output channel 10 | func processData(input <-chan string, output chan<- int, errors chan<- error) { 11 | for value := range input { 12 | number, err := strconv.Atoi(value) 13 | if err != nil { 14 | errors <- fmt.Errorf("invalid input: %s", value) 15 | continue 16 | } 17 | output <- number * number // Squaring the number 18 | } 19 | } 20 | 21 | func main() { 22 | inputData := []string{"4", "9", "a", "16"} 23 | inputChan := make(chan string) 24 | outputChan := make(chan int) 25 | errorChan := make(chan error) 26 | 27 | go processData(inputChan, outputChan, errorChan) 28 | 29 | // Sending data to be processed 30 | go func() { 31 | for _, val := range inputData { 32 | inputChan <- val 33 | } 34 | close(inputChan) 35 | }() 36 | 37 | // Receiving and handling output 38 | for i := 0; i < len(inputData); i++ { 39 | select { 40 | case result := <-outputChan: 41 | fmt.Println("Processed result:", result) 42 | case err := <-errorChan: 43 | fmt.Println("Error:", err) 44 | } 45 | } 46 | 47 | // Using time.Sleep to prevent the program from exiting immediately 48 | time.Sleep(1 * time.Second) 49 | } 50 | -------------------------------------------------------------------------------- /05-build-internals-memory-management/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/siinghd/05 2 | 3 | go 1.21.5 4 | -------------------------------------------------------------------------------- /06-pointers/README.md: -------------------------------------------------------------------------------- 1 | ## What Are Pointers? 2 | 3 | Pointers are variables that store the address of another variable. They enable direct manipulation of memory and can lead to more efficient code. 4 | 5 | ## Basic Pointer Operations 6 | 7 | ### Declaring and Initializing Pointers 8 | 9 | - Declare a pointer with an asterisk (*) before the type: `var p *int`. 10 | - Initialize a pointer using the address-of operator (`&`): `p = &variable`. 11 | 12 | ### Dereferencing Pointers 13 | 14 | - Access the value at the pointer’s address using `*`: `value = *p`. 15 | 16 | ### The `nil` Pointer 17 | 18 | - A pointer that is not assigned any address holds the value `nil`. 19 | - Dereferencing a `nil` pointer will cause a runtime panic. 20 | 21 | ## Pointer Arithmetic 22 | 23 | - Go does not support arithmetic on pointers to prevent unsafe memory manipulation, enhancing program safety and maintainability. 24 | 25 | ## Common Use Cases and Best Practices 26 | 27 | ### Efficiency in Function Calls 28 | 29 | - Use pointers to pass large structs or arrays to functions without copying the entire data. 30 | 31 | ### Modifying Function Arguments 32 | 33 | - Change the original variable by passing a pointer to the function. 34 | 35 | ### Method Receivers 36 | 37 | - Use pointer receivers to modify the struct’s fields within methods. 38 | 39 | ### Edge Cases and Pitfalls 40 | 41 | - **Uninitialized Pointers**: An uninitialized pointer (`var p *int`) is `nil` and can cause runtime panic if dereferenced. 42 | - **Pointer to Pointer**: Pointers can point to other pointers, leading to complex structures. 43 | - **Pointer to Interface**: Be cautious when using pointers to interfaces, as interfaces themselves can hold pointers. 44 | 45 | ## Advanced Examples 46 | 47 | ### Modifying Array Elements 48 | 49 | ```go 50 | func modifyArray(arr *[3]int) { 51 | (*arr)[1] = 10 // Modify the second element of the array 52 | } 53 | 54 | func main() { 55 | arr := [3]int{1, 2, 3} 56 | modifyArray(&arr) 57 | fmt.Println(arr) // Output: [1 10 3] 58 | } 59 | ``` 60 | 61 | ### Working with Structs 62 | 63 | ```go 64 | type Person struct { 65 | Name string 66 | Age int 67 | } 68 | 69 | func incrementAge(p *Person) { 70 | p.Age++ 71 | } 72 | 73 | func main() { 74 | person := Person{Name: "Alice", Age: 30} 75 | incrementAge(&person) 76 | fmt.Println(person) // Output: {Alice 31} 77 | } 78 | ``` 79 | 80 | ### Edge Case: Passing Slice to a Function 81 | 82 | - Slices are reference types and inherently contain a pointer to an array. When passing a slice to a function, modifications to the slice elements change the original slice. 83 | 84 | ```go 85 | func modifySlice(s []int) { 86 | s[0] = 99 87 | } 88 | 89 | func main() { 90 | mySlice := []int{1, 2, 3} 91 | modifySlice(mySlice) 92 | fmt.Println(mySlice) // Output: [99 2 3] 93 | } 94 | ``` 95 | 96 | 97 | -------------------------------------------------------------------------------- /06-pointers/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // Person struct for the struct example 6 | type Person struct { 7 | Name string 8 | Age int 9 | } 10 | 11 | // incrementAge increments the age of a Person 12 | func incrementAge(p *Person) { 13 | p.Age++ 14 | } 15 | 16 | // modifyArray modifies the specified element of an array 17 | func modifyArray(arr *[3]int) { 18 | (*arr)[1] = 10 // Modify the second element of the array 19 | } 20 | 21 | // modifySlice modifies the first element of a slice 22 | func modifySlice(s []int) { 23 | s[0] = 99 24 | } 25 | 26 | func main() { 27 | // Example: Basic Pointer Operations 28 | var a int = 20 29 | var p *int = &a 30 | fmt.Println("Value of a:", a) // Value of a 31 | fmt.Println("Address of a:", p) // Address of a 32 | fmt.Println("Value at address p:", *p) // Value at address p 33 | 34 | // Example: Modifying Array Elements 35 | arr := [3]int{1, 2, 3} 36 | modifyArray(&arr) 37 | fmt.Println("Modified array:", arr) 38 | 39 | // Example: Working with Structs 40 | person := Person{Name: "Alice", Age: 30} 41 | incrementAge(&person) 42 | fmt.Println("Modified struct:", person) 43 | 44 | // Example: Edge Case with Slices 45 | mySlice := []int{1, 2, 3} 46 | modifySlice(mySlice) 47 | fmt.Println("Modified slice:", mySlice) 48 | } 49 | -------------------------------------------------------------------------------- /06-pointers/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/siinghd/06 2 | 3 | go 1.21.5 4 | -------------------------------------------------------------------------------- /07-arrays-slices-structs-map/README.md: -------------------------------------------------------------------------------- 1 | # Part 1 : Arrays 2 | 3 | ## What are Arrays 4 | 5 | - An array is a fundamental data structure used in programming. 6 | - It's a collection of elements, each identified by at least one array index or key. 7 | - Arrays can hold multiple items that are usually of the same data type, like integers, strings, or objects, and they are stored in contiguous memory locations. 8 | - The elements can be accessed randomly by indexing into the array. 9 | 10 | ### Basic Declaration 11 | 12 | - An array is declared with a fixed length and type. 13 | - Syntax: `var arrayName [size]Type` 14 | 15 | ### Initializing Arrays 16 | 17 | - Arrays can be initialized with specific values. 18 | - Example: `arr := [3]int{1, 2, 3}` 19 | 20 | ### ... Array Literals 21 | 22 | - Arrays can be initialized using array literals. 23 | - Example: `arr := [...]int{1, 2, 3, 4}` (The compiler determines the length) 24 | 25 | ## Operations on Arrays 26 | 27 | ### Accessing Elements 28 | 29 | - Access elements by their index: `value := arr[0]` 30 | 31 | ### Iterating over Arrays 32 | 33 | - Use `for` or `for range` loops to iterate over arrays. 34 | 35 | ### Multidimensional Arrays 36 | 37 | - Declare using multiple square brackets: `var matrix [3][3]int` 38 | 39 | ## Arrays and Functions 40 | 41 | ### Passing Arrays to Functions 42 | 43 | - Arrays are passed by value, which means a copy is passed. 44 | - To avoid copying, use pointers or slices. 45 | 46 | ### Returning Arrays from Functions 47 | 48 | - Functions can return arrays, but this is often inefficient due to copying. 49 | 50 | ## Advanced Topics and Best Practices 51 | 52 | ### When to Use Arrays 53 | 54 | - Use when the number of elements is known at compile time. 55 | - Prefer slices for more flexible data structures. 56 | 57 | ### Arrays vs. Slices 58 | 59 | - Slices are more common and versatile compared to arrays. 60 | - Slices are reference types, while arrays are value types. 61 | 62 | ### Caveats and Pitfalls 63 | 64 | - **Size as Part of the Type**: In Go, the size of the array is part of its type. This means `[3]int` and `[4]int` are different types. 65 | - **Copy Behavior**: Assigning one array to another copies all the elements. 66 | - **Large Arrays**: Large arrays can be costly to copy and can consume a lot of stack space. 67 | 68 | ## Examples 69 | 70 | ### Example 1: Basic Array Operations 71 | 72 | ```go 73 | package main 74 | 75 | import "fmt" 76 | 77 | func main() { 78 | var arr [5]int 79 | arr[0] = 1 80 | arr[4] = 5 81 | fmt.Println("Array:", arr) 82 | } 83 | ``` 84 | 85 | ### Example 2: Iterating Over Arrays 86 | 87 | ```go 88 | func printArray(arr [5]int) { 89 | for i, value := range arr { 90 | fmt.Println("Index:", i, "Value:", value) 91 | } 92 | } 93 | 94 | func main() { 95 | arr := [5]int{1, 2, 3, 4, 5} 96 | printArray(arr) 97 | } 98 | ``` 99 | 100 | ### Example 3: Multidimensional Array 101 | 102 | ```go 103 | func main() { 104 | var matrix [2][2]int 105 | matrix[0][0] = 1 106 | matrix[0][1] = 2 107 | matrix[1][0] = 3 108 | matrix[1][1] = 4 109 | fmt.Println("Matrix:", matrix) 110 | } 111 | ``` 112 | 113 | # Part 2: Slices 114 | 115 | ## What Are Slices? 116 | 117 | - Slices are a flexible, dynamic view into the elements of an array. 118 | - They provide more functionality compared to arrays. 119 | 120 | ## Creating and Using Slices 121 | 122 | ### Declaring Slices 123 | 124 | - Slices are declared without specifying a size. 125 | - Example: `var slice []int` 126 | 127 | ### Initializing Slices 128 | 129 | - Created with `make`: `slice := make([]int, length, capacity)` 130 | - Slice literals: `slice := []int{1, 2, 3}` 131 | 132 | ### Slicing an Array 133 | 134 | - Slices can be formed by "slicing" an array: `slice := array[start:end]` 135 | 136 | ## Operations on Slices 137 | 138 | ### Appending to Slices 139 | 140 | - Use `append`: `slice = append(slice, element)` 141 | 142 | ### Copying Slices 143 | 144 | - `copy` function: `copy(destSlice, srcSlice)` 145 | 146 | ### Reslicing 147 | 148 | - Slices can be resliced: `newSlice := slice[start:end]` 149 | 150 | ## Slices in Functions 151 | 152 | - Slices are reference types and can be modified within functions. 153 | 154 | ## Advanced Topics and Best Practices 155 | 156 | ### Capacity and Length 157 | 158 | - Understanding the difference between length and capacity. 159 | - Capacity grows automatically when appending beyond capacity. 160 | 161 | ### Memory Efficiency 162 | 163 | - Overusing `append` can lead to performance issues due to frequent reallocations. 164 | - Preallocate slices when the size is known in advance. 165 | 166 | ### Slices and Concurrency 167 | 168 | - Slices are not thread-safe; use proper synchronization when using slices in concurrent environments. 169 | 170 | ### Caveats and Pitfalls 171 | 172 | - **Modifying Underlying Array**: Modifying the elements of a slice modifies the underlying array, which can affect other slices. 173 | - **Slice Zero Value**: The zero value of a slice is `nil`. 174 | - **Slice Bounds**: Exceeding slice bounds can cause runtime panics. 175 | 176 | ## Examples 177 | 178 | ### Example 1: Basic Slice Operations 179 | 180 | ```go 181 | package main 182 | 183 | import "fmt" 184 | 185 | func main() { 186 | slice := []int{1, 2, 3} 187 | fmt.Println("Initial Slice:", slice) 188 | 189 | // Appending to slice 190 | slice = append(slice, 4) 191 | fmt.Println("Slice after append:", slice) 192 | } 193 | ``` 194 | 195 | ### Example 2: Modifying Slices in Functions 196 | 197 | ```go 198 | func addToEachElement(slice []int, addValue int) { 199 | for i := range slice { 200 | slice[i] += addValue 201 | } 202 | } 203 | 204 | func main() { 205 | slice := []int{1, 2, 3} 206 | addToEachElement(slice, 1) 207 | fmt.Println("Modified Slice:", slice) 208 | } 209 | ``` 210 | 211 | ### Example 3: Slice Capacity and Reslicing 212 | 213 | ```go 214 | func main() { 215 | slice := make([]int, 3, 5) 216 | fmt.Println("Length:", len(slice), "Capacity:", cap(slice)) 217 | 218 | // Reslicing 219 | newSlice := slice[:cap(slice)] 220 | newSlice[3] = 4 221 | newSlice[4] = 5 222 | fmt.Println("Resliced Slice:", newSlice) 223 | } 224 | ``` 225 | # Part 3: Structs 226 | 227 | ## What Are Structs? 228 | 229 | - Structs are composite data types that group together variables (fields) under one name. 230 | - They are used to create custom data types, making data handling more structured and clear. 231 | 232 | ## Defining and Instantiating Structs 233 | 234 | ### Basic Definition 235 | 236 | - Syntax for defining a struct: `type StructName struct { Fields }` 237 | - Example: `type Person struct { Name string; Age int }` 238 | 239 | ### Creating Instances 240 | 241 | - Instantiating a struct: `person := Person{Name: "Alice", Age: 30}` 242 | 243 | ### Pointers to Structs 244 | 245 | - Using pointers to work with structs efficiently and to modify their fields within functions. 246 | 247 | ## Struct Operations 248 | 249 | ### Accessing and Modifying Fields 250 | 251 | - Accessing fields: `person.Name` 252 | - Modifying fields: `person.Age = 31` 253 | 254 | ### Comparing Structs 255 | 256 | - Structs are comparable if each field is comparable. 257 | - Use `==` to compare struct instances. 258 | 259 | ### Nested Structs 260 | 261 | - Structs can contain other structs, enabling complex data structures. 262 | 263 | ## Advanced Topics 264 | 265 | ### Anonymous (Embedded) Structs 266 | 267 | - Embedding one struct within another without a field name. 268 | - Promotes fields and methods to the embedding struct. 269 | 270 | ### Tags 271 | 272 | - Struct field tags provide metadata for fields, commonly used with encoding/decoding libraries. 273 | 274 | ### Structs and Interfaces 275 | 276 | - Implementing interfaces with structs. 277 | - Using structs to provide concrete implementations of interface methods. 278 | 279 | ## Best Practices 280 | 281 | - Use structs to model real-world data and entities. 282 | - Keep struct design simple and cohesive. 283 | - Use pointer receivers in methods to modify struct fields or for efficiency with large structs. 284 | 285 | ## Examples 286 | 287 | ### Example 1: Basic Struct Usage 288 | 289 | ```go 290 | package main 291 | 292 | import "fmt" 293 | 294 | type Person struct { 295 | Name string 296 | Age int 297 | } 298 | 299 | func main() { 300 | person := Person{Name: "Alice", Age: 30} 301 | fmt.Println("Person:", person) 302 | } 303 | ``` 304 | 305 | ### Example 2: Nested Structs and Tags 306 | 307 | ```go 308 | type Address struct { 309 | City, State string 310 | } 311 | 312 | type Person struct { 313 | Name string 314 | Age int 315 | Address Address 316 | } 317 | 318 | func main() { 319 | person := Person{ 320 | Name: "Bob", 321 | Age: 35, 322 | Address: Address{ 323 | City: "New York", 324 | State: "NY", 325 | }, 326 | } 327 | fmt.Println("Person with Address:", person) 328 | } 329 | ``` 330 | 331 | ### Example 3: Structs with Methods 332 | 333 | ```go 334 | type Rectangle struct { 335 | Width, Height float64 336 | } 337 | 338 | // Method to calculate area 339 | func (r Rectangle) Area() float64 { 340 | return r.Width * r.Height 341 | } 342 | 343 | func main() { 344 | rect := Rectangle{Width: 10, Height: 5} 345 | fmt.Println("Area:", rect.Area()) 346 | } 347 | ``` 348 | # Part 4: Map 349 | 350 | ## What Are Maps? 351 | 352 | - Maps are a collection of key-value pairs where each key is unique. 353 | - They are similar to dictionaries or hash tables in other languages. 354 | 355 | ## Creating and Using Maps 356 | 357 | ### Declaring Maps 358 | 359 | - Maps are declared using the `map` keyword. 360 | - Syntax: `var mapName map[KeyType]ValueType` 361 | - Example: `var employeeSalary map[string]int` 362 | 363 | ### Initializing Maps 364 | 365 | - Maps can be initialized using the `make` function: `employeeSalary = make(map[string]int)` 366 | - Map literals: `employeeSalary := map[string]string{"John": "Developer", "Emma": "Designer"}` 367 | 368 | ### Adding and Updating Elements 369 | 370 | - Add or update elements using the key: `employeeSalary["Mike"] = 50000` 371 | 372 | ### Retrieving Elements 373 | 374 | - Retrieve elements by their key: `salary := employeeSalary["Mike"]` 375 | 376 | ### Checking for Existence 377 | 378 | - Use the "comma ok" idiom to check if a key exists: `salary, ok := employeeSalary["Mike"]` 379 | 380 | ## Advanced Map Operations 381 | 382 | ### Iterating over Maps 383 | 384 | - Use a `for range` loop to iterate over maps. 385 | - Iteration order over maps is not guaranteed. 386 | 387 | ### Deleting Elements 388 | 389 | - Remove elements using the `delete` function: `delete(employeeSalary, "Mike")` 390 | 391 | ### Maps and Concurrency 392 | 393 | - Maps are not safe for concurrent use without additional synchronization. 394 | - Use sync.RWMutex for concurrent read/write access. 395 | 396 | ## Best Practices and Common Pitfalls 397 | 398 | ### Choosing Key Types 399 | 400 | - Use comparable types for keys (int, string, etc.). 401 | - Avoid using slices or other non-comparable types as keys. 402 | 403 | ### Nil Maps 404 | 405 | - A nil map behaves like an empty map when reading, but attempts to write to a nil map will cause a runtime panic. 406 | 407 | ### Memory Efficiency 408 | 409 | - Preallocate maps if the size is approximately known. 410 | 411 | ## Examples 412 | 413 | ### Example 1: Basic Map Operations 414 | 415 | ```go 416 | package main 417 | 418 | import "fmt" 419 | 420 | func main() { 421 | employeeSalary := make(map[string]int) 422 | employeeSalary["John"] = 30000 423 | employeeSalary["Emma"] = 45000 424 | 425 | fmt.Println("Employee Salaries:", employeeSalary) 426 | } 427 | ``` 428 | 429 | ### Example 2: Iterating and Deleting 430 | 431 | ```go 432 | func main() { 433 | colors := map[string]string{"Red": "#FF0000", "Green": "#00FF00", "Blue": "#0000FF"} 434 | 435 | // Iterating over the map 436 | for color, hex := range colors { 437 | fmt.Println(color, ":", hex) 438 | } 439 | 440 | // Deleting an element 441 | delete(colors, "Green") 442 | 443 | fmt.Println("Updated Colors:", colors) 444 | } 445 | ``` 446 | 447 | ### Example 3: Maps with Struct Values 448 | 449 | ```go 450 | type Employee struct { 451 | ID int 452 | Salary int 453 | } 454 | 455 | func main() { 456 | employees := make(map[string]Employee) 457 | employees["John"] = Employee{ID: 1, Salary: 30000} 458 | employees["Emma"] = Employee{ID: 2, Salary: 45000} 459 | 460 | for name, info := range employees { 461 | fmt.Printf("%s: %+v\n", name, info) 462 | } 463 | } 464 | ``` 465 | 466 | 467 | -------------------------------------------------------------------------------- /07-arrays-slices-structs-map/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | // Part 1: Arrays 10 | type FixedSizeData [5]int // An array type to store fixed size data 11 | 12 | // Part 2: Slices 13 | type DynamicData []int // A slice type to store dynamic data 14 | 15 | // Part 3: Structs 16 | type DataRecord struct { 17 | ID int 18 | Fixed FixedSizeData 19 | Dynamic DynamicData 20 | } 21 | 22 | // Generates random fixed size data 23 | func generateFixedSizeData() FixedSizeData { 24 | var data FixedSizeData 25 | for i := range data { 26 | data[i] = rand.Intn(100) // Random number between 0 and 99 27 | } 28 | return data 29 | } 30 | 31 | // Generates random dynamic data 32 | func generateDynamicData(size int) DynamicData { 33 | data := make(DynamicData, size) 34 | for i := range data { 35 | data[i] = rand.Intn(100) // Random number between 0 and 99 36 | } 37 | return data 38 | } 39 | 40 | type Employee struct { 41 | ID int 42 | Salary int 43 | } 44 | 45 | func main() { 46 | rand.New(rand.NewSource(time.Now().UnixNano())) 47 | // Creating an array of DataRecord structs 48 | records := make([]DataRecord, 3) 49 | 50 | for i := range records { 51 | records[i] = DataRecord{ 52 | ID: i + 1, 53 | Fixed: generateFixedSizeData(), 54 | Dynamic: generateDynamicData(rand.Intn(5) + 1), // Dynamic size between 1 and 5 55 | } 56 | } 57 | 58 | // Displaying the data records 59 | for _, record := range records { 60 | fmt.Printf("Record %d:\nFixed Data: %v\nDynamic Data: %v\n\n", record.ID, record.Fixed, record.Dynamic) 61 | } 62 | 63 | employees := make(map[string]Employee) 64 | employees["John"] = Employee{ID: 1, Salary: 30000} 65 | employees["Emma"] = Employee{ID: 2, Salary: 45000} 66 | 67 | for name, info := range employees { 68 | fmt.Printf("%s: %+v\n", name, info) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /07-arrays-slices-structs-map/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/siinghd/07 2 | 3 | go 1.21.5 4 | -------------------------------------------------------------------------------- /08-make-new-other-keywords/README.md: -------------------------------------------------------------------------------- 1 | ## Understanding `make` 2 | 3 | - `make` is used to initialize slices, maps, and channels. 4 | - Unlike `new`, `make` returns initialized (not zeroed) memory. 5 | - Syntax: `make(Type, size[, capacity])` 6 | 7 | ### Using `make` 8 | 9 | #### Slices 10 | ```go 11 | slice := make([]int, 5) // Slice of integers with length 5 12 | ``` 13 | 14 | #### Maps 15 | ```go 16 | map := make(map[string]int) // Map with string keys and integer values 17 | ``` 18 | 19 | #### Channels 20 | ```go 21 | ch := make(chan int) // Channel of integers 22 | ``` 23 | 24 | ## Understanding `new` 25 | 26 | - `new` allocates memory and returns a pointer to a zeroed value of the specified type. 27 | - Useful when you need a pointer to a value. 28 | - Syntax: `new(Type)` 29 | 30 | ### Using `new` 31 | 32 | #### Example 33 | ```go 34 | p := new(int) // Pointer to an integer, initialized to zero 35 | ``` 36 | 37 | ## Other Important Keywords 38 | 39 | ### `defer` 40 | - Delays the execution of a function until the surrounding function returns. 41 | - Commonly used for cleanup operations. 42 | 43 | ### `go` 44 | - Starts a new goroutine. 45 | - Essential for concurrency in Go. 46 | 47 | ### `struct` 48 | - Defines a new composite data type. 49 | - Useful for grouping related data together. 50 | 51 | ### `interface` 52 | - Specifies a set of method signatures. 53 | - Used for polymorphism and to define behavior. 54 | 55 | ## Best Practices and Common Pitfalls 56 | 57 | - Use `make` when you need to work with the internal structure of slices, maps, or channels. 58 | - Use `new` when you need a pointer to a value and don't require it to be immediately initialized. 59 | - Be cautious with `defer` in loops - it can lead to resource exhaustion. 60 | - Understand the difference between concurrency (with `go`) and parallelism. 61 | 62 | ## Examples 63 | 64 | ### Using `make` and `new` 65 | 66 | ```go 67 | package main 68 | 69 | import "fmt" 70 | 71 | func main() { 72 | // Using make 73 | slice := make([]int, 5) 74 | fmt.Println("Slice:", slice) 75 | 76 | // Using new 77 | p := new(int) 78 | fmt.Println("Pointer to int:", *p) 79 | } 80 | ``` 81 | 82 | ### Using `defer` and `go` 83 | 84 | ```go 85 | package main 86 | 87 | import ( 88 | "fmt" 89 | "sync" 90 | ) 91 | 92 | func main() { 93 | // Using defer 94 | defer fmt.Println("Deferred call") 95 | 96 | // Using go 97 | var wg sync.WaitGroup 98 | wg.Add(1) 99 | go func() { 100 | defer wg.Done() 101 | fmt.Println("Goroutine call") 102 | }() 103 | wg.Wait() 104 | } 105 | ``` 106 | 107 | 108 | -------------------------------------------------------------------------------- /08-make-new-other-keywords/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | // Initializing a pointer to an int 7 | ptr := new(int) 8 | fmt.Println("Pointer:", ptr) // Outputs the memory address 9 | fmt.Println("Value at pointer:", *ptr) // Outputs the value (0) 10 | 11 | // Assigning a value to the location pointed by the pointer 12 | *ptr = 100 13 | fmt.Println("New value at pointer:", *ptr) 14 | 15 | slice := make([]int, 3) // Length and capacity are 3 16 | slice[0] = 10 17 | slice[1] = 20 18 | slice[2] = 30 19 | fmt.Println(slice) 20 | 21 | employeeSalary := make(map[string]int) 22 | employeeSalary["John"] = 40000 23 | employeeSalary["Jane"] = 45000 24 | fmt.Println(employeeSalary) 25 | 26 | ch := make(chan int, 2) // Buffered channel of size 2 27 | ch <- 1 28 | ch <- 2 29 | fmt.Println(<-ch) 30 | fmt.Println(<-ch) 31 | } 32 | -------------------------------------------------------------------------------- /08-make-new-other-keywords/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/siinghd/08 2 | 3 | go 1.21.5 4 | -------------------------------------------------------------------------------- /09-if-else-loops-switch/README.md: -------------------------------------------------------------------------------- 1 | # Part 1: In-Depth Look at `if`, `else if`, and `else` in Go 2 | 3 | ## Basic `if` Statement 4 | 5 | - The `if` statement evaluates a condition and executes a block of code if the condition is true. 6 | - Syntax: 7 | ```go 8 | if condition { 9 | // code to execute if condition is true 10 | } 11 | ``` 12 | 13 | ### Examples 14 | 15 | ```go 16 | if x > 0 { 17 | fmt.Println("x is positive") 18 | } 19 | ``` 20 | 21 | ## Using `else if` and `else` 22 | 23 | - `else if` allows you to check multiple conditions. 24 | - `else` executes a block of code when none of the `if` or `else if` conditions are true. 25 | 26 | ### Example 27 | 28 | ```go 29 | if score > 90 { 30 | fmt.Println("Grade: A") 31 | } else if score > 75 { 32 | fmt.Println("Grade: B") 33 | } else { 34 | fmt.Println("Grade: C or below") 35 | } 36 | ``` 37 | 38 | ## Short Statement Syntax 39 | 40 | - `if` statements can include a short statement to execute before the condition. 41 | - Commonly used for initializing a variable needed for the condition. 42 | 43 | ### Example 44 | 45 | ```go 46 | if v := getValue(); v > 0 { 47 | fmt.Println("Positive value:", v) 48 | } else { 49 | fmt.Println("Non-positive value:", v) 50 | } 51 | ``` 52 | 53 | ## Caveats and Best Practices 54 | 55 | ### Caveats 56 | 57 | - **Unintended Shadowing**: Be cautious of variable shadowing, especially in the short statement syntax. 58 | - **Complex Conditions**: Avoid overly complex conditions. Consider breaking them into multiple `if` statements or functions. 59 | 60 | ### Best Practices 61 | 62 | - **Readability Over Brevity**: Aim for clarity. Sometimes, an extra line of code makes your intention clearer. 63 | - **Avoid Deep Nesting**: Deeply nested `if` statements can make code hard to read and maintain. Consider using early returns or switch statements. 64 | - **Boolean Expressions**: Directly use boolean expressions in conditions instead of comparing them to `true` or `false`. 65 | 66 | ### Advanced Example: Combining `if` with Function Calls 67 | 68 | ```go 69 | func checkStatus(statusCode int) string { 70 | if msg, ok := statusMessages[statusCode]; ok { 71 | return msg 72 | } 73 | return "Unknown status" 74 | } 75 | 76 | func main() { 77 | fmt.Println(checkStatus(404)) // "Not Found" 78 | fmt.Println(checkStatus(999)) // "Unknown status" 79 | } 80 | ``` 81 | 82 | In this example, `checkStatus` uses an `if` statement with a map lookup to return a corresponding message. It's a common pattern in Go to handle such cases concisely. 83 | 84 | 85 | - **Modifying Collection in Loop**: Avoid modifying a slice or array while iterating over it with `range`. This can lead to unexpected behavior. 86 | - **Ignoring the Range Value**: If you don't need the index or value, use `_` to ignore it: `for _, value := range collection`. 87 | 88 | ### Advanced Example: Nested Loops and Labels 89 | 90 | ```go 91 | outerLoop: 92 | for i := 0; i < 3; i++ { 93 | for j := 0; j < 3; j++ { 94 | if i+j == 4 { 95 | break outerLoop 96 | } 97 | fmt.Printf("i = %d, j = %d\n", i, j) 98 | } 99 | } 100 | ``` 101 | # Part 2: Loops in Go 102 | 103 | ## Introduction to Loops 104 | 105 | Loops in Go are a way to execute a block of code repeatedly. Go simplifies looping constructs by providing only the `for` loop, which can be used in various ways to achieve different types of iteration. 106 | 107 | ## Basic `for` Loop 108 | 109 | - The most basic form of the `for` loop consists of three components: initialization, condition, and post-statement. 110 | - Syntax: 111 | ```go 112 | for initialization; condition; post-statement { 113 | // code to be executed 114 | } 115 | ``` 116 | 117 | ### Example 118 | 119 | ```go 120 | for i := 0; i < 5; i++ { 121 | fmt.Println("Loop iteration:", i) 122 | } 123 | ``` 124 | 125 | ## `for` as a While Loop 126 | 127 | - In Go, the `for` loop can also function as a `while` loop by omitting the initialization and post-statement. 128 | - Syntax: 129 | ```go 130 | for condition { 131 | // code to be executed 132 | } 133 | ``` 134 | 135 | ### Example 136 | 137 | ```go 138 | x := 0 139 | for x < 5 { 140 | fmt.Println("x is less than 5") 141 | x++ 142 | } 143 | ``` 144 | 145 | ## Infinite Loops 146 | 147 | - An infinite loop runs forever unless it is stopped by a `break` statement or an external interrupt. 148 | - Syntax: 149 | ```go 150 | for { 151 | // infinite loop 152 | } 153 | ``` 154 | 155 | ### Example 156 | 157 | ```go 158 | for { 159 | fmt.Println("Infinite loop") 160 | time.Sleep(1 * time.Second) 161 | } 162 | ``` 163 | 164 | ## `for range` Loop 165 | 166 | - The `for range` form of the loop iterates over elements in a variety of data structures. 167 | - It's commonly used with slices, arrays, maps, and channels. 168 | - Syntax: 169 | ```go 170 | for key, value := range collection { 171 | // code to be executed 172 | } 173 | ``` 174 | 175 | ### Example 176 | 177 | ```go 178 | fruits := []string{"apple", "banana", "cherry"} 179 | for index, fruit := range fruits { 180 | fmt.Printf("Index: %d, Fruit: %s\n", index, fruit) 181 | } 182 | ``` 183 | 184 | ## Best Practices and Common Pitfalls 185 | 186 | ### Best Practices 187 | 188 | - **Use `range` for Iterating Collections**: Prefer `for range` for cleaner and more readable code when iterating over slices, arrays, and maps. 189 | - **Avoid Infinite Loops**: Be cautious with loop conditions to prevent unintentional infinite loops. 190 | 191 | ### Pitfalls 192 | 193 | - **Modifying Collection in Loop**: Avoid modifying a slice or array while iterating over it with `range`. This can lead to unexpected behavior. 194 | - **Ignoring the Range Value**: If you don't need the index or value, use `_` to ignore it: `for _, value := range collection`. 195 | 196 | ### Advanced Example: Nested Loops and Labels 197 | 198 | ```go 199 | outerLoop: 200 | for i := 0; i < 3; i++ { 201 | for j := 0; j < 3; j++ { 202 | if i+j == 4 { 203 | break outerLoop 204 | } 205 | fmt.Printf("i = %d, j = %d\n", i, j) 206 | } 207 | } 208 | ``` 209 | 210 | In this example, nested loops are used with labels to break out of the outer loop from within the inner loop. 211 | 212 | Moving on to Part 3 of the tutorial, we will focus on the `switch` statement in Go. This part will cover the usage of `switch` for conditional branching, including its standard form, `switch` with a short statement, and type switches. We'll also discuss best practices and common pitfalls. 213 | 214 | ### Enhanced Tutorial Part 3: Mastering the `Switch` Statement in Go 215 | 216 | --- 217 | 218 | # Part 3: `Switch` Statements in Go 219 | 220 | ## Basic `Switch` Statement 221 | 222 | - `switch` evaluates an expression and executes the `case` block that matches the result. 223 | - Syntax: 224 | ```go 225 | switch expression { 226 | case value1: 227 | // code block for value1 228 | case value2: 229 | // code block for value2 230 | default: 231 | // code block if no case matches 232 | } 233 | ``` 234 | 235 | ### Example 236 | 237 | ```go 238 | dayOfWeek := 3 239 | switch dayOfWeek { 240 | case 1: 241 | fmt.Println("Monday") 242 | case 2: 243 | fmt.Println("Tuesday") 244 | case 3: 245 | fmt.Println("Wednesday") 246 | default: 247 | fmt.Println("Other day") 248 | } 249 | ``` 250 | 251 | ## `Switch` with Short Statement 252 | 253 | - Like `if`, `switch` can start with a short statement before evaluating the expression. 254 | - Useful for initializing a value used in the switch expression. 255 | 256 | ### Example 257 | 258 | ```go 259 | switch day := getDayOfWeek(); day { 260 | case "Sat", "Sun": 261 | fmt.Println("It's the weekend") 262 | default: 263 | fmt.Println("It's a weekday") 264 | } 265 | ``` 266 | 267 | ## Type Switches 268 | 269 | - A type switch compares types instead of values. 270 | - Useful for handling values of different types in a concise way. 271 | 272 | ### Example 273 | 274 | ```go 275 | func getType(i interface{}) { 276 | switch i.(type) { 277 | case int: 278 | fmt.Println("i is an int") 279 | case string: 280 | fmt.Println("i is a string") 281 | default: 282 | fmt.Println("i is another type") 283 | } 284 | } 285 | 286 | getType(21) 287 | getType("hello") 288 | ``` 289 | 290 | ## Best Practices and Common Pitfalls 291 | 292 | ### Best Practices 293 | 294 | - **Simplifying Multiple Conditions**: Use `switch` to simplify code that requires multiple `if`-`else` statements. 295 | - **Grouping Cases**: Multiple cases can be grouped together if they share the same code block. 296 | 297 | ### Pitfalls 298 | 299 | - **Forgetting `break` Statements**: Unlike some languages, Go automatically breaks out of a `switch` after a case succeeds. Be aware of this to avoid fall-through errors. 300 | - **Overusing Type Switches**: While type switches are powerful, overusing them can lead to code that's hard to understand. Use them judiciously. 301 | 302 | ### Advanced Example: Using Fallthrough 303 | 304 | ```go 305 | score := 85 306 | switch { 307 | case score >= 90: 308 | fmt.Println("Grade: A") 309 | fallthrough 310 | case score >= 80: 311 | fmt.Println("Grade: B") 312 | fallthrough 313 | case score >= 70: 314 | fmt.Println("Grade: C") 315 | default: 316 | fmt.Println("Grade: D") 317 | } 318 | ``` 319 | 320 | In this example, `fallthrough` is used to explicitly continue executing the following cases. It demonstrates an advanced use of `switch`, though it should be used sparingly to avoid confusing code. 321 | 322 | 323 | -------------------------------------------------------------------------------- /09-if-else-loops-switch/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | // Part 1: Conditional Statements - `if`, `else if`, `else` 7 | age := 25 8 | if age < 18 { 9 | fmt.Println("You are a minor.") 10 | } else if age >= 18 && age < 60 { 11 | fmt.Println("You are an adult.") 12 | } else { 13 | fmt.Println("You are a senior.") 14 | } 15 | 16 | // Part 2: Loops - `for` and `for range` 17 | // Basic for loop 18 | for i := 0; i < 5; i++ { 19 | fmt.Println("Loop iteration:", i) 20 | } 21 | 22 | // for range loop 23 | fruits := []string{"apple", "banana", "cherry"} 24 | for index, fruit := range fruits { 25 | fmt.Printf("Index: %d, Fruit: %s\n", index, fruit) 26 | } 27 | 28 | // Part 3: The `switch` statement 29 | // Basic switch 30 | dayOfWeek := 3 31 | switch dayOfWeek { 32 | case 1: 33 | fmt.Println("Monday") 34 | case 2: 35 | fmt.Println("Tuesday") 36 | case 3: 37 | fmt.Println("Wednesday") 38 | default: 39 | fmt.Println("Other day") 40 | } 41 | 42 | // Switch with a short statement 43 | switch today := dayOfWeek; today { 44 | case 1, 2, 3, 4, 5: 45 | fmt.Println("It's a weekday") 46 | case 6, 7: 47 | fmt.Println("It's the weekend") 48 | default: 49 | fmt.Println("Invalid day") 50 | } 51 | 52 | // Type switch 53 | var i interface{} = 10.0 54 | switch v := i.(type) { 55 | case int: 56 | fmt.Println("i is an int:", v) 57 | case float64: 58 | fmt.Println("i is a float64:", v) 59 | case string: 60 | fmt.Println("i is a string:", v) 61 | default: 62 | fmt.Println("i is another type") 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /09-if-else-loops-switch/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/siinghd/09 2 | 3 | go 1.21.5 4 | -------------------------------------------------------------------------------- /10-functions-methods/README.md: -------------------------------------------------------------------------------- 1 | # Part 1: Functions in Go 2 | 3 | Functions are the building blocks of Go code. They allow you to encapsulate code logic and promote reusability. Understanding functions in Go is key to writing effective and organized code. 4 | 5 | ## Basic Function Declaration 6 | 7 | ### Syntax 8 | 9 | - Functions are declared using the `func` keyword. 10 | - Syntax: 11 | ```go 12 | func functionName(parameterList) returnType { 13 | // function body 14 | } 15 | ``` 16 | 17 | ### Parameters and Return Types 18 | 19 | - Functions can have zero or more parameters. 20 | - Functions can return one or more values. 21 | - Named return values can be used for documentation purposes. 22 | 23 | ### Examples 24 | 25 | ```go 26 | func greet(name string) string { 27 | return "Hello " + name 28 | } 29 | 30 | func add(a, b int) (result int) { 31 | result = a + b 32 | return // Named return 33 | } 34 | ``` 35 | 36 | ## Variadic Functions 37 | 38 | - Variadic functions can accept a variable number of arguments. 39 | - Defined by appending `...` before the type of the last parameter. 40 | 41 | ### Example 42 | 43 | ```go 44 | func sum(numbers ...int) int { 45 | total := 0 46 | for _, num := range numbers { 47 | total += num 48 | } 49 | return total 50 | } 51 | ``` 52 | 53 | ## Anonymous Functions and Closures 54 | 55 | ### Anonymous Functions 56 | 57 | - Functions without a name. 58 | - Can be assigned to a variable or passed as an argument. 59 | 60 | ### Closures 61 | 62 | - Anonymous functions that capture variables from the surrounding scope. 63 | - Useful for creating function generators or maintaining state. 64 | 65 | ### Examples 66 | 67 | ```go 68 | // Anonymous function 69 | adder := func(a, b int) int { 70 | return a + b 71 | } 72 | fmt.Println(adder(5, 7)) 73 | 74 | // Closure 75 | incrementor := func() func() int { 76 | i := 0 77 | return func() int { 78 | i++ 79 | return i 80 | } 81 | } 82 | inc := incrementor() 83 | fmt.Println(inc()) // 1 84 | fmt.Println(inc()) // 2 85 | ``` 86 | 87 | ## Passing Functions as Arguments 88 | 89 | - Functions can be passed as arguments to other functions. 90 | - Enables higher-order functions. 91 | 92 | ### Example 93 | 94 | ```go 95 | func applyOperation(a, b int, operation func(int, int) int) int { 96 | return operation(a, b) 97 | } 98 | 99 | result := applyOperation(3, 4, adder) 100 | fmt.Println("Result:", result) 101 | ``` 102 | 103 | ## Best Practices and Common Pitfalls 104 | 105 | ### Best Practices 106 | 107 | - Keep functions focused on a single task. 108 | - Use descriptive names for functions and parameters. 109 | - Prefer shorter, readable functions over longer, complex ones. 110 | 111 | ### Pitfalls 112 | 113 | - Avoid too many parameters in function signatures; consider using structs if the number of parameters is large. 114 | - Beware of using named returns in long functions as they can reduce readability. 115 | 116 | 117 | # Part 2: Methods in Go 118 | 119 | Methods in Go attach a function to a specific type, allowing you to define behavior associated with that type. Understanding methods is crucial for structuring and organizing code in Go, especially when working with custom types and interfaces. 120 | 121 | ## Defining Methods 122 | 123 | ### Syntax 124 | 125 | - Methods are functions with a special receiver argument. 126 | - Syntax: 127 | ```go 128 | func (receiver ReceiverType) MethodName(params) returnType { 129 | // method body 130 | } 131 | ``` 132 | 133 | ### Value vs. Pointer Receivers 134 | 135 | - A method can have either a value receiver or a pointer receiver. 136 | - Value receivers operate on a copy of the original type. 137 | - Pointer receivers can modify the original value. 138 | 139 | ### Example 140 | 141 | ```go 142 | type Circle struct { 143 | Radius float64 144 | } 145 | 146 | // Method with a value receiver 147 | func (c Circle) Area() float64 { 148 | return math.Pi * c.Radius * c.Radius 149 | } 150 | 151 | // Method with a pointer receiver 152 | func (c *Circle) Scale(factor float64) { 153 | c.Radius *= factor 154 | } 155 | ``` 156 | 157 | ## Embedding and Composition 158 | 159 | - Go supports composition over inheritance, achieved through embedding. 160 | - Embedding a type allows access to its methods and fields. 161 | 162 | ### Example 163 | 164 | ```go 165 | type Shape struct { 166 | X, Y float64 167 | } 168 | 169 | type Circle struct { 170 | Shape 171 | Radius float64 172 | } 173 | 174 | // Method on the embedded Shape type 175 | func (s *Shape) Move(dx, dy float64) { 176 | s.X += dx 177 | s.Y += dy 178 | } 179 | ``` 180 | 181 | ## Best Practices and Common Pitfalls 182 | 183 | ### Best Practices 184 | 185 | - Use pointer receivers to avoid copying on method calls or to allow methods to modify the receiving struct. 186 | - Keep method receivers consistent for a given type (either all pointer receivers or all value receivers). 187 | 188 | ### Pitfalls 189 | 190 | - Avoid embedding types with overlapping method sets, as it can lead to ambiguous calls. 191 | - Be cautious of value receivers for large structs as they can be inefficient due to copying. 192 | 193 | ### Advanced Example: Composition and Method Overriding 194 | 195 | ```go 196 | type Rectangle struct { 197 | Shape 198 | Width, Height float64 199 | } 200 | 201 | // Method overriding 202 | func (r Rectangle) Area() float64 { 203 | return r.Width * r.Height 204 | } 205 | 206 | func main() { 207 | rect := Rectangle{Width: 4, Height: 3} 208 | rect.Move(1, 1) // Accessing Move method from embedded Shape 209 | fmt.Println("Area of rectangle:", rect.Area()) 210 | } 211 | ``` 212 | 213 | In this example, `Rectangle` embeds `Shape` and overrides the `Area` method. The `Move` method from `Shape` is accessible from a `Rectangle` instance. 214 | 215 | -------------------------------------------------------------------------------- /10-functions-methods/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | // Part 1: Functions 9 | 10 | // Basic function that adds two integers 11 | func add(a, b int) int { 12 | return a + b 13 | } 14 | 15 | // Variadic function that sums an arbitrary number of integers 16 | func sum(nums ...int) int { 17 | total := 0 18 | for _, num := range nums { 19 | total += num 20 | } 21 | return total 22 | } 23 | 24 | // Anonymous function assigned to a variable 25 | var double = func(n int) int { 26 | return n * 2 27 | } 28 | 29 | // Part 2: Methods 30 | 31 | // Struct for demonstrating methods 32 | type Circle struct { 33 | Radius float64 34 | } 35 | 36 | // Method with a value receiver 37 | func (c Circle) Area() float64 { 38 | return math.Pi * c.Radius * c.Radius 39 | } 40 | 41 | // Method with a pointer receiver 42 | func (c *Circle) Scale(factor float64) { 43 | c.Radius *= factor 44 | } 45 | 46 | func main() { 47 | // Demonstrating functions 48 | fmt.Println("Add 3 and 5:", add(3, 5)) 49 | fmt.Println("Sum of 2, 4, and 6:", sum(2, 4, 6)) 50 | fmt.Println("Double 4:", double(4)) 51 | 52 | // Demonstrating methods 53 | c := Circle{Radius: 5} 54 | fmt.Println("Circle area:", c.Area()) 55 | c.Scale(2) 56 | fmt.Println("Scaled circle radius:", c.Radius) 57 | } 58 | -------------------------------------------------------------------------------- /10-functions-methods/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/siinghd/10 2 | 3 | go 1.21.5 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Go Language Tutorials 3 | 4 | ## Introduction 5 | 6 | This repository contains tutorials and examples for learning the Go programming language, designed for developers new to Go, covering basic to advanced topics. 7 | 8 | ### How to Install 9 | 10 | 1. **Install Go**: Visit [Go's official website](https://go.dev/dl/) for download and installation instructions. 11 | 2. **Clone This Repository**: Use Git to clone this repository to your local machine. 12 | 3. **Navigate to Specific Directory**: Go to the directory of the tutorial you're interested in. 13 | 4. **Follow the README.md**: Each tutorial has its own README with specific instructions. 14 | 15 | ## Understanding `go.mod` 16 | 17 | `go.mod` is vital for Go's module system, introduced in Go 1.11. It's used for declaring modules, managing dependencies, and ensuring consistent builds. 18 | 19 | ### Managing Libraries with `go.mod` 20 | 21 | - **Adding a Library**: Use `go get `. 22 | - **Updating a Library**: Run `go get -u `. 23 | - **Removing a Library**: Remove the import statement and run `go mod tidy`. 24 | 25 | ## Using `go help` 26 | 27 | `go help` provides documentation on Go's tools and concepts. Use it for general help, command-specific information, and understanding Go concepts. 28 | 29 | ### Updating Go 30 | 31 | To update Go: 32 | 33 | 1. **Check the Latest Version**: Visit Go's official website. 34 | 2. **Download and Install**: Follow the installation process for the new version. 35 | 36 | ## Running and Building Go Programs 37 | 38 | ### Running a Program 39 | 40 | - **Execute Directly**: Use `go run .go` to compile and run the program. 41 | 42 | ### Building a Program 43 | 44 | - **Compile to Binary**: Use `go build .go`. This generates an executable binary. 45 | 46 | ### Best Practices for Production 47 | 48 | - **Testing**: Ensure thorough testing of your code. 49 | - **Dependencies**: Manage dependencies carefully, using `go.mod` for reproducibility. 50 | - **Security**: Keep your dependencies up to date to avoid security vulnerabilities. 51 | - **Configuration**: Use environment variables or configuration files for settings. 52 | - **Logging**: Implement proper logging for monitoring and debugging. 53 | - **Performance**: Profile and optimize your code for better performance. 54 | - **Documentation**: Maintain clear documentation for ease of maintenance and onboarding. 55 | 56 | ## Contributing 57 | 58 | Contributions to this tutorial series are welcome. Read the contribution(coming soon) guidelines before making a pull request. 59 | 60 | 61 | ## About the Author 62 | 63 | I am Harpreet Singh, a full-stack developer with an interest in DevOps, leading a team of developers and exploring new technologies. 64 | --------------------------------------------------------------------------------