diff --git a/01_hello_world.go b/01_hello_world.go new file mode 100644 index 0000000..cef5e69 --- /dev/null +++ b/01_hello_world.go @@ -0,0 +1,12 @@ +// 01_hello_world.go - Basic Go program structure +package main + +import "fmt" + +func main() { + fmt.Println("Hello, Go!") + fmt.Println("This is a basic Go program demonstrating:") + fmt.Println("- Package declaration") + fmt.Println("- Import statement") + fmt.Println("- Main function") +} diff --git a/02_data_types.go b/02_data_types.go new file mode 100644 index 0000000..588ff05 --- /dev/null +++ b/02_data_types.go @@ -0,0 +1,55 @@ +// 02_data_types.go - Demonstrates all basic data types in Go +package main + +import ( + "fmt" +) + +func main() { + fmt.Println("=== GO DATA TYPES DEMONSTRATION ===") + + // Numeric Types + var myInt int = 42 + var myInt8 int8 = 127 + var myInt16 int16 = 32767 + var myInt32 int32 = 2147483647 + var myInt64 int64 = 9223372036854775807 + + var myUint uint = 42 + var myUint8 uint8 = 255 + var myByte byte = 255 // byte is alias for uint8 + + var myFloat32 float32 = 3.14 + var myFloat64 float64 = 3.141592653589793 + + var myComplex64 complex64 = 1 + 2i + var myComplex128 complex128 = 1 + 2i + + // String and Rune + var myString string = "Hello, Go!" + var myRune rune = 'A' // rune is alias for int32 + + // Boolean + var myBool bool = true + + // Print all types + fmt.Printf("Int: %d (type: %T)\n", myInt, myInt) + fmt.Printf("Int8: %d (type: %T)\n", myInt8, myInt8) + fmt.Printf("Int16: %d (type: %T)\n", myInt16, myInt16) + fmt.Printf("Int32: %d (type: %T)\n", myInt32, myInt32) + fmt.Printf("Int64: %d (type: %T)\n", myInt64, myInt64) + + fmt.Printf("Uint: %d (type: %T)\n", myUint, myUint) + fmt.Printf("Uint8: %d (type: %T)\n", myUint8, myUint8) + fmt.Printf("Byte: %d (type: %T)\n", myByte, myByte) + + fmt.Printf("Float32: %.2f (type: %T)\n", myFloat32, myFloat32) + fmt.Printf("Float64: %.15f (type: %T)\n", myFloat64, myFloat64) + + fmt.Printf("Complex64: %g (type: %T)\n", myComplex64, myComplex64) + fmt.Printf("Complex128: %g (type: %T)\n", myComplex128, myComplex128) + + fmt.Printf("String: %s (type: %T)\n", myString, myString) + fmt.Printf("Rune: %c (type: %T)\n", myRune, myRune) + fmt.Printf("Boolean: %t (type: %T)\n", myBool, myBool) +} diff --git a/03_zero_values.go b/03_zero_values.go new file mode 100644 index 0000000..d2b7deb --- /dev/null +++ b/03_zero_values.go @@ -0,0 +1,51 @@ +// 03_zero_values.go - Demonstrates zero values in Go +package main + +import "fmt" + +type Person struct { + Name string + Age int +} + +func main() { + fmt.Println("=== GO ZERO VALUES DEMONSTRATION ===") + + // Basic types zero values + var ( + b bool + i int + f float64 + c complex128 + s string + r rune + bt byte + ) + + fmt.Printf("bool zero value: %t\n", b) + fmt.Printf("int zero value: %d\n", i) + fmt.Printf("float64 zero value: %f\n", f) + fmt.Printf("complex128 zero value: %g\n", c) + fmt.Printf("string zero value: '%s' (empty string)\n", s) + fmt.Printf("rune zero value: %d\n", r) + fmt.Printf("byte zero value: %d\n", bt) + + // Composite types zero values + var ( + slice []int + dmap map[string]int + ptr *int + iface interface{} + ch chan int + fn func() + person Person + ) + + fmt.Printf("\nslice zero value: %v (nil: %t)\n", slice, slice == nil) + fmt.Printf("map zero value: %v (nil: %t)\n", dmap, dmap == nil) + fmt.Printf("pointer zero value: %v (nil: %t)\n", ptr, ptr == nil) + fmt.Printf("interface zero value: %v (nil: %t)\n", iface, iface == nil) + fmt.Printf("channel zero value: %v (nil: %t)\n", ch, ch == nil) + fmt.Printf("function zero value: %v (nil: %t)\n", fn, fn == nil) + fmt.Printf("struct zero value: %+v\n", person) +} diff --git a/04_arrays_slices.go b/04_arrays_slices.go new file mode 100644 index 0000000..844345f --- /dev/null +++ b/04_arrays_slices.go @@ -0,0 +1,67 @@ +// 04_arrays_slices.go - Demonstrates arrays vs slices +package main + +import "fmt" + +func main() { + fmt.Println("=== ARRAYS VS SLICES DEMONSTRATION ===") + + // Arrays - Fixed size + fmt.Println("--- ARRAYS ---") + var arr1 [5]int // Declaration with zero values + arr2 := [5]int{1, 2, 3, 4, 5} // Declaration with initialization + arr3 := [...]int{10, 20, 30} // Let compiler count the elements + + fmt.Printf("arr1: %v (length: %d)\n", arr1, len(arr1)) + fmt.Printf("arr2: %v (length: %d)\n", arr2, len(arr2)) + fmt.Printf("arr3: %v (length: %d)\n", arr3, len(arr3)) + + // Modifying array elements + arr1[0] = 100 + fmt.Printf("arr1 after modification: %v\n", arr1) + + // Slices - Dynamic size + fmt.Println("\n--- SLICES ---") + var slice1 []int // nil slice + slice2 := []int{1, 2, 3, 4, 5} // slice literal + slice3 := make([]int, 5) // make function with length + slice4 := make([]int, 3, 10) // make with length and capacity + + fmt.Printf("slice1: %v (len: %d, cap: %d, nil: %t)\n", slice1, len(slice1), cap(slice1), slice1 == nil) + fmt.Printf("slice2: %v (len: %d, cap: %d)\n", slice2, len(slice2), cap(slice2)) + fmt.Printf("slice3: %v (len: %d, cap: %d)\n", slice3, len(slice3), cap(slice3)) + fmt.Printf("slice4: %v (len: %d, cap: %d)\n", slice4, len(slice4), cap(slice4)) + + // Slicing operations + fmt.Println("\n--- SLICING OPERATIONS ---") + numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} + fmt.Printf("Original: %v\n", numbers) + fmt.Printf("numbers[2:5]: %v\n", numbers[2:5]) + fmt.Printf("numbers[:4]: %v\n", numbers[:4]) + fmt.Printf("numbers[6:]: %v\n", numbers[6:]) + fmt.Printf("numbers[:]: %v\n", numbers[:]) + + // Append operations + fmt.Println("\n--- APPEND OPERATIONS ---") + var dynamicSlice []int + fmt.Printf("Initial: %v (len: %d, cap: %d)\n", dynamicSlice, len(dynamicSlice), cap(dynamicSlice)) + + dynamicSlice = append(dynamicSlice, 1) + fmt.Printf("After append(1): %v (len: %d, cap: %d)\n", dynamicSlice, len(dynamicSlice), cap(dynamicSlice)) + + dynamicSlice = append(dynamicSlice, 2, 3, 4) + fmt.Printf("After append(2,3,4): %v (len: %d, cap: %d)\n", dynamicSlice, len(dynamicSlice), cap(dynamicSlice)) + + moreNumbers := []int{5, 6, 7} + dynamicSlice = append(dynamicSlice, moreNumbers...) + fmt.Printf("After append(slice...): %v (len: %d, cap: %d)\n", dynamicSlice, len(dynamicSlice), cap(dynamicSlice)) + + // Copy operations + fmt.Println("\n--- COPY OPERATIONS ---") + src := []int{1, 2, 3, 4, 5} + dst := make([]int, len(src)) + n := copy(dst, src) + fmt.Printf("Source: %v\n", src) + fmt.Printf("Destination: %v\n", dst) + fmt.Printf("Elements copied: %d\n", n) +} diff --git a/05_type_conversions.go b/05_type_conversions.go new file mode 100644 index 0000000..2abcf6f --- /dev/null +++ b/05_type_conversions.go @@ -0,0 +1,95 @@ +// 05_type_conversions.go - Demonstrates type conversions and assertions +package main + +import ( + "fmt" + "strconv" +) + +func main() { + fmt.Println("=== TYPE CONVERSIONS DEMONSTRATION ===") + + // Numeric conversions + fmt.Println("--- NUMERIC CONVERSIONS ---") + var i int = 42 + var f float64 = float64(i) + var u uint = uint(f) + + fmt.Printf("int: %d -> float64: %f -> uint: %d\n", i, f, u) + + // String conversions + fmt.Println("\n--- STRING CONVERSIONS ---") + + // Number to string + num := 123 + str1 := strconv.Itoa(num) + str2 := fmt.Sprintf("%d", num) + + fmt.Printf("Number %d to string: '%s' (using Itoa)\n", num, str1) + fmt.Printf("Number %d to string: '%s' (using Sprintf)\n", num, str2) + + // String to number + str := "456" + num2, err := strconv.Atoi(str) + if err != nil { + fmt.Printf("Error converting string to int: %v\n", err) + } else { + fmt.Printf("String '%s' to number: %d\n", str, num2) + } + + // Float conversions + floatStr := "3.14159" + floatNum, err := strconv.ParseFloat(floatStr, 64) + if err != nil { + fmt.Printf("Error converting string to float: %v\n", err) + } else { + fmt.Printf("String '%s' to float: %f\n", floatStr, floatNum) + } + + // Boolean conversions + boolStr := "true" + boolVal, err := strconv.ParseBool(boolStr) + if err != nil { + fmt.Printf("Error converting string to bool: %v\n", err) + } else { + fmt.Printf("String '%s' to bool: %t\n", boolStr, boolVal) + } + + // Type assertions with interfaces + fmt.Println("\n--- TYPE ASSERTIONS ---") + var data interface{} = "Hello, Go!" + + // Type assertion with ok pattern + if str, ok := data.(string); ok { + fmt.Printf("data is a string: '%s'\n", str) + } else { + fmt.Println("data is not a string") + } + + // Type assertion without ok pattern (can panic) + str3 := data.(string) + fmt.Printf("Direct assertion: '%s'\n", str3) + + // Type switch + fmt.Println("\n--- TYPE SWITCH ---") + testTypeSwitch(42) + testTypeSwitch("Hello") + testTypeSwitch(3.14) + testTypeSwitch(true) + testTypeSwitch([]int{1, 2, 3}) +} + +func testTypeSwitch(data interface{}) { + switch v := data.(type) { + case int: + fmt.Printf("Integer: %d\n", v) + case string: + fmt.Printf("String: '%s'\n", v) + case float64: + fmt.Printf("Float64: %f\n", v) + case bool: + fmt.Printf("Boolean: %t\n", v) + default: + fmt.Printf("Unknown type: %T with value: %v\n", v, v) + } +} diff --git a/06_error_handling.go b/06_error_handling.go new file mode 100644 index 0000000..0293221 --- /dev/null +++ b/06_error_handling.go @@ -0,0 +1,134 @@ +// 06_error_handling.go - Demonstrates error handling patterns in Go +package main + +import ( + "errors" + "fmt" + "strconv" +) + +// Custom error type +type ValidationError struct { + Field string + Message string +} + +func (e *ValidationError) Error() string { + return fmt.Sprintf("validation error in field '%s': %s", e.Field, e.Message) +} + +// Function that returns an error +func divide(a, b float64) (float64, error) { + if b == 0 { + return 0, errors.New("division by zero") + } + return a / b, nil +} + +// Function with custom error +func validateAge(age int) error { + if age < 0 { + return &ValidationError{ + Field: "age", + Message: "age cannot be negative", + } + } + if age > 150 { + return &ValidationError{ + Field: "age", + Message: "age seems unrealistic", + } + } + return nil +} + +// Function that wraps errors +func parseAndValidateAge(ageStr string) (int, error) { + age, err := strconv.Atoi(ageStr) + if err != nil { + return 0, fmt.Errorf("failed to parse age '%s': %w", ageStr, err) + } + + if err := validateAge(age); err != nil { + return 0, fmt.Errorf("age validation failed: %w", err) + } + + return age, nil +} + +func main() { + fmt.Println("=== ERROR HANDLING DEMONSTRATION ===") + + // Basic error handling + fmt.Println("--- BASIC ERROR HANDLING ---") + result, err := divide(10, 2) + if err != nil { + fmt.Printf("Error: %v\n", err) + } else { + fmt.Printf("10 / 2 = %.2f\n", result) + } + + result, err = divide(10, 0) + if err != nil { + fmt.Printf("Error: %v\n", err) + } else { + fmt.Printf("10 / 0 = %.2f\n", result) + } + + // Custom error handling + fmt.Println("\n--- CUSTOM ERROR HANDLING ---") + testAges := []int{25, -5, 200} + for _, age := range testAges { + if err := validateAge(age); err != nil { + fmt.Printf("Age %d: %v\n", age, err) + + // Type assertion to check for custom error + if ve, ok := err.(*ValidationError); ok { + fmt.Printf(" Custom error - Field: %s, Message: %s\n", ve.Field, ve.Message) + } + } else { + fmt.Printf("Age %d: valid\n", age) + } + } + + // Error wrapping + fmt.Println("\n--- ERROR WRAPPING ---") + testAgeStrings := []string{"25", "abc", "-10", "200"} + for _, ageStr := range testAgeStrings { + age, err := parseAndValidateAge(ageStr) + if err != nil { + fmt.Printf("Age string '%s': %v\n", ageStr, err) + + // Unwrap the error to check the underlying cause + if unwrapped := errors.Unwrap(err); unwrapped != nil { + fmt.Printf(" Underlying error: %v\n", unwrapped) + } + } else { + fmt.Printf("Age string '%s': parsed to %d\n", ageStr, age) + } + } + + // Multiple return values for error handling + fmt.Println("\n--- MULTIPLE RETURN VALUES ---") + value, success := safeDivide(20, 4) + if success { + fmt.Printf("20 / 4 = %.2f\n", value) + } else { + fmt.Println("Division failed") + } + + value, success = safeDivide(20, 0) + if success { + fmt.Printf("20 / 0 = %.2f\n", value) + } else { + fmt.Println("Division failed") + } +} + +// Alternative error handling using boolean +func safeDivide(a, b float64) (float64, bool) { + if b == 0 { + return 0, false + } + return a / b, true +} diff --git a/07_goroutines_basic.go b/07_goroutines_basic.go new file mode 100644 index 0000000..2c428d9 --- /dev/null +++ b/07_goroutines_basic.go @@ -0,0 +1,73 @@ +// 07_goroutines_basic.go - Basic goroutines demonstration +package main + +import ( + "fmt" + "runtime" + "time" +) + +func sayHello(name string) { + for i := 0; i < 5; i++ { + fmt.Printf("Hello from %s - %d\n", name, i) + time.Sleep(100 * time.Millisecond) + } +} + +func countNumbers(name string) { + for i := 1; i <= 5; i++ { + fmt.Printf("%s counting: %d\n", name, i) + time.Sleep(200 * time.Millisecond) + } +} + +func main() { + fmt.Println("=== GOROUTINES BASIC DEMONSTRATION ===") + + // Show initial number of goroutines + fmt.Printf("Initial goroutines: %d\n", runtime.NumGoroutine()) + + // Sequential execution (normal function calls) + fmt.Println("\n--- SEQUENTIAL EXECUTION ---") + start := time.Now() + sayHello("Sequential-1") + sayHello("Sequential-2") + fmt.Printf("Sequential execution took: %v\n", time.Since(start)) + + // Concurrent execution using goroutines + fmt.Println("\n--- CONCURRENT EXECUTION ---") + start = time.Now() + + go sayHello("Goroutine-1") + go sayHello("Goroutine-2") + go countNumbers("Counter") + + // Show number of goroutines after launching + fmt.Printf("Goroutines after launch: %d\n", runtime.NumGoroutine()) + + // Wait for goroutines to complete + time.Sleep(1 * time.Second) + + fmt.Printf("Concurrent execution took: %v\n", time.Since(start)) + fmt.Printf("Final goroutines: %d\n", runtime.NumGoroutine()) + + // Anonymous goroutines + fmt.Println("\n--- ANONYMOUS GOROUTINES ---") + + go func() { + fmt.Println("Anonymous goroutine 1 started") + time.Sleep(300 * time.Millisecond) + fmt.Println("Anonymous goroutine 1 finished") + }() + + go func(msg string) { + fmt.Printf("Anonymous goroutine 2: %s\n", msg) + time.Sleep(200 * time.Millisecond) + fmt.Println("Anonymous goroutine 2 finished") + }("Hello from closure!") + + // Wait for anonymous goroutines + time.Sleep(500 * time.Millisecond) + + fmt.Println("Main function ending...") +} diff --git a/08_channels_basic.go b/08_channels_basic.go new file mode 100644 index 0000000..7f3afa6 --- /dev/null +++ b/08_channels_basic.go @@ -0,0 +1,98 @@ +// 08_channels_basic.go - Basic channels demonstration +package main + +import ( + "fmt" + "time" +) + +func main() { + fmt.Println("=== CHANNELS BASIC DEMONSTRATION ===") + + // Unbuffered channels + fmt.Println("--- UNBUFFERED CHANNELS ---") + ch := make(chan string) + + go func() { + time.Sleep(1 * time.Second) + ch <- "Hello from goroutine!" + }() + + fmt.Println("Waiting for message...") + message := <-ch + fmt.Printf("Received: %s\n", message) + + // Buffered channels + fmt.Println("\n--- BUFFERED CHANNELS ---") + bufferedCh := make(chan int, 3) + + // Send values without blocking + bufferedCh <- 1 + bufferedCh <- 2 + bufferedCh <- 3 + + fmt.Printf("Channel length: %d, capacity: %d\n", len(bufferedCh), cap(bufferedCh)) + + // Receive values + for i := 0; i < 3; i++ { + value := <-bufferedCh + fmt.Printf("Received: %d\n", value) + } + + // Channel direction (send-only, receive-only) + fmt.Println("\n--- CHANNEL DIRECTIONS ---") + dataCh := make(chan string, 2) + + go sender(dataCh) + go receiver(dataCh) + + time.Sleep(2 * time.Second) + + // Closing channels + fmt.Println("\n--- CLOSING CHANNELS ---") + numberCh := make(chan int) + + go func() { + for i := 1; i <= 5; i++ { + numberCh <- i + time.Sleep(200 * time.Millisecond) + } + close(numberCh) // Close the channel when done + }() + + // Receive until channel is closed + for number := range numberCh { + fmt.Printf("Received number: %d\n", number) + } + + fmt.Println("Channel closed, range loop ended") + + // Check if channel is closed + testCh := make(chan int, 1) + testCh <- 42 + close(testCh) + + value, ok := <-testCh + fmt.Printf("Value: %d, Channel open: %t\n", value, ok) + + value, ok = <-testCh + fmt.Printf("Value: %d, Channel open: %t\n", value, ok) +} + +// Function that only sends to channel +func sender(ch chan<- string) { + messages := []string{"First", "Second", "Third"} + for _, msg := range messages { + ch <- msg + time.Sleep(300 * time.Millisecond) + } + close(ch) +} + +// Function that only receives from channel +func receiver(ch <-chan string) { + for msg := range ch { + fmt.Printf("Receiver got: %s\n", msg) + } + fmt.Println("Receiver finished") +} diff --git a/09_channels_select.go b/09_channels_select.go new file mode 100644 index 0000000..90414da --- /dev/null +++ b/09_channels_select.go @@ -0,0 +1,124 @@ +// 09_channels_select.go - Select statement with channels +package main + +import ( + "fmt" + "time" +) + +func main() { + fmt.Println("=== CHANNELS SELECT DEMONSTRATION ===") + + // Basic select statement + fmt.Println("--- BASIC SELECT ---") + ch1 := make(chan string) + ch2 := make(chan string) + + go func() { + time.Sleep(1 * time.Second) + ch1 <- "Message from channel 1" + }() + + go func() { + time.Sleep(2 * time.Second) + ch2 <- "Message from channel 2" + }() + + // Select will choose the first channel that's ready + for i := 0; i < 2; i++ { + select { + case msg1 := <-ch1: + fmt.Printf("Received from ch1: %s\n", msg1) + case msg2 := <-ch2: + fmt.Printf("Received from ch2: %s\n", msg2) + } + } + + // Select with default case + fmt.Println("\n--- SELECT WITH DEFAULT ---") + ch3 := make(chan string) + + go func() { + time.Sleep(2 * time.Second) + ch3 <- "Delayed message" + }() + + for i := 0; i < 5; i++ { + select { + case msg := <-ch3: + fmt.Printf("Received: %s\n", msg) + default: + fmt.Printf("No message available (attempt %d)\n", i+1) + time.Sleep(500 * time.Millisecond) + } + } + + // Select with timeout + fmt.Println("\n--- SELECT WITH TIMEOUT ---") + slowCh := make(chan string) + + go func() { + time.Sleep(3 * time.Second) + slowCh <- "This message is too slow" + }() + + select { + case msg := <-slowCh: + fmt.Printf("Received: %s\n", msg) + case <-time.After(1 * time.Second): + fmt.Println("Timeout! No message received within 1 second") + } + + // Multiple channel communication + fmt.Println("\n--- MULTIPLE CHANNEL COMMUNICATION ---") + numbers := make(chan int) + strings := make(chan string) + done := make(chan bool) + + // Producer goroutines + go func() { + for i := 1; i <= 3; i++ { + numbers <- i + time.Sleep(300 * time.Millisecond) + } + close(numbers) + }() + + go func() { + words := []string{"apple", "banana", "cherry"} + for _, word := range words { + strings <- word + time.Sleep(400 * time.Millisecond) + } + close(strings) + }() + + // Consumer + go func() { + numbersOpen := true + stringsOpen := true + + for numbersOpen || stringsOpen { + select { + case num, ok := <-numbers: + if ok { + fmt.Printf("Received number: %d\n", num) + } else { + fmt.Println("Numbers channel closed") + numbersOpen = false + } + case str, ok := <-strings: + if ok { + fmt.Printf("Received string: %s\n", str) + } else { + fmt.Println("Strings channel closed") + stringsOpen = false + } + } + } + done <- true + }() + + <-done + fmt.Println("All channels processed") +} diff --git a/10_range_keyword.go b/10_range_keyword.go new file mode 100644 index 0000000..aa99ea1 --- /dev/null +++ b/10_range_keyword.go @@ -0,0 +1,111 @@ +// 10_range_keyword.go - Demonstrates the range keyword +package main + +import "fmt" + +func main() { + fmt.Println("=== RANGE KEYWORD DEMONSTRATION ===") + + // Range over arrays and slices + fmt.Println("--- RANGE OVER SLICE ---") + numbers := []int{10, 20, 30, 40, 50} + + // Range with both index and value + for index, value := range numbers { + fmt.Printf("Index: %d, Value: %d\n", index, value) + } + + // Range with only value (ignore index with _) + fmt.Println("\nUsing only values:") + for _, value := range numbers { + fmt.Printf("Value: %d\n", value) + } + + // Range with only index + fmt.Println("\nUsing only indices:") + for index := range numbers { + fmt.Printf("Index: %d\n", index) + } + + // Range over maps + fmt.Println("\n--- RANGE OVER MAP ---") + person := map[string]interface{}{ + "name": "Alice", + "age": 30, + "city": "New York", + "married": true, + } + + for key, value := range person { + fmt.Printf("Key: %s, Value: %v (Type: %T)\n", key, value, value) + } + + // Range over strings (iterates over runes) + fmt.Println("\n--- RANGE OVER STRING ---") + text := "Hello, 世界" + + fmt.Println("Character by character:") + for index, runeValue := range text { + fmt.Printf("Index: %d, Rune: %c, Unicode: %U\n", index, runeValue, runeValue) + } + + // Range over channels + fmt.Println("\n--- RANGE OVER CHANNEL ---") + ch := make(chan int) + + // Start a goroutine to send data + go func() { + for i := 1; i <= 5; i++ { + ch <- i * i // Send squares + } + close(ch) // Important: close the channel when done + }() + + // Range over channel (receives until channel is closed) + fmt.Println("Receiving squares from channel:") + for value := range ch { + fmt.Printf("Received: %d\n", value) + } + + // Range with arrays + fmt.Println("\n--- RANGE OVER ARRAY ---") + colors := [4]string{"red", "green", "blue", "yellow"} + + for i, color := range colors { + fmt.Printf("Color %d: %s\n", i, color) + } + + // Range with slice of structs + fmt.Println("\n--- RANGE OVER SLICE OF STRUCTS ---") + type Person struct { + Name string + Age int + } + + people := []Person{ + {"Alice", 30}, + {"Bob", 25}, + {"Charlie", 35}, + } + + for index, person := range people { + fmt.Printf("Person %d: %s (age %d)\n", index, person.Name, person.Age) + } + + // Modifying slice elements (be careful with range) + fmt.Println("\n--- MODIFYING ELEMENTS ---") + nums := []int{1, 2, 3, 4, 5} + fmt.Printf("Original: %v\n", nums) + + // This won't modify the original slice because range creates copies + for _, num := range nums { + num = num * 2 + } + fmt.Printf("After range modification (won't work): %v\n", nums) + + // Correct way to modify elements + for i := range nums { + nums[i] = nums[i] * 2 + } + fmt.Printf("After index-based modification: %v\n", nums) +} diff --git a/11_structs_methods.go b/11_structs_methods.go new file mode 100644 index 0000000..fea653e --- /dev/null +++ b/11_structs_methods.go @@ -0,0 +1,167 @@ +// 11_structs_methods.go - Demonstrates structs and methods +package main + +import "fmt" + +// Basic struct definition +type Person struct { + Name string + Age int + Email string + Address Address // Embedded struct +} + +// Nested struct +type Address struct { + Street string + City string + Country string +} + +// Method with value receiver +func (p Person) GetFullInfo() string { + return fmt.Sprintf("%s (%d years old) - %s", p.Name, p.Age, p.Email) +} + +// Method with pointer receiver (can modify the struct) +func (p *Person) UpdateAge(newAge int) { + p.Age = newAge +} + +// Method with pointer receiver +func (p *Person) UpdateEmail(newEmail string) { + p.Email = newEmail +} + +// Method on Address struct +func (a Address) GetFullAddress() string { + return fmt.Sprintf("%s, %s, %s", a.Street, a.City, a.Country) +} + +func main() { + fmt.Println("=== STRUCTS AND METHODS DEMONSTRATION ===") + + // Creating structs + fmt.Println("--- CREATING STRUCTS ---") + + // Method 1: Field by field + var person1 Person + person1.Name = "Alice" + person1.Age = 30 + person1.Email = "alice@example.com" + person1.Address = Address{ + Street: "123 Main St", + City: "New York", + Country: "USA", + } + + // Method 2: Struct literal + person2 := Person{ + Name: "Bob", + Age: 25, + Email: "bob@example.com", + Address: Address{ + Street: "456 Oak Ave", + City: "Los Angeles", + Country: "USA", + }, + } + + // Method 3: Using field names (order doesn't matter) + person3 := Person{ + Email: "charlie@example.com", + Name: "Charlie", + Age: 35, + Address: Address{ + Country: "Canada", + City: "Toronto", + Street: "789 Pine Rd", + }, + } + + // Method 4: Positional (not recommended for readability) + address4 := Address{"321 Elm St", "Chicago", "USA"} + person4 := Person{"Diana", 28, "diana@example.com", address4} + + fmt.Printf("Person 1: %+v\n", person1) + fmt.Printf("Person 2: %+v\n", person2) + fmt.Printf("Person 3: %+v\n", person3) + fmt.Printf("Person 4: %+v\n", person4) + + // Using methods + fmt.Println("\n--- USING METHODS ---") + fmt.Printf("Person 1 info: %s\n", person1.GetFullInfo()) + fmt.Printf("Person 1 address: %s\n", person1.Address.GetFullAddress()) + + // Method with pointer receiver + fmt.Println("\n--- POINTER RECEIVER METHODS ---") + fmt.Printf("Before update - %s\n", person2.GetFullInfo()) + + person2.UpdateAge(26) + person2.UpdateEmail("bob.updated@example.com") + + fmt.Printf("After update - %s\n", person2.GetFullInfo()) + + // Anonymous structs + fmt.Println("\n--- ANONYMOUS STRUCTS ---") + car := struct { + Brand string + Model string + Year int + }{ + Brand: "Toyota", + Model: "Camry", + Year: 2023, + } + + fmt.Printf("Car: %+v\n", car) + + // Struct comparison + fmt.Println("\n--- STRUCT COMPARISON ---") + addr1 := Address{"123 Main St", "New York", "USA"} + addr2 := Address{"123 Main St", "New York", "USA"} + addr3 := Address{"456 Oak Ave", "Los Angeles", "USA"} + + fmt.Printf("addr1 == addr2: %t\n", addr1 == addr2) + fmt.Printf("addr1 == addr3: %t\n", addr1 == addr3) + + // Struct embedding (composition) + fmt.Println("\n--- STRUCT EMBEDDING ---") + type Employee struct { + Person // Embedded struct + Position string + Salary float64 + Department string + } + + employee := Employee{ + Person: Person{ + Name: "John", + Age: 32, + Email: "john@company.com", + Address: Address{ + Street: "100 Business Blvd", + City: "San Francisco", + Country: "USA", + }, + }, + Position: "Software Engineer", + Salary: 95000.0, + Department: "Engineering", + } + + fmt.Printf("Employee: %+v\n", employee) + fmt.Printf("Employee name (direct access): %s\n", employee.Name) // Can access embedded fields directly + fmt.Printf("Employee info: %s\n", employee.GetFullInfo()) // Can call embedded methods directly + + // Struct tags (used with JSON, XML, etc.) + fmt.Println("\n--- STRUCT TAGS ---") + type Product struct { + ID int `json:"id" xml:"id"` + Name string `json:"name" xml:"name"` + Price float64 `json:"price" xml:"price"` + } + + product := Product{ID: 1, Name: "Laptop", Price: 999.99} + fmt.Printf("Product with tags: %+v\n", product) +} diff --git a/12_interfaces.go b/12_interfaces.go new file mode 100644 index 0000000..50337ba --- /dev/null +++ b/12_interfaces.go @@ -0,0 +1,164 @@ +// 12_interfaces.go - Demonstrates interfaces in Go +package main + +import ( + "fmt" + "math" +) + +// Define interfaces +type Shape interface { + Area() float64 + Perimeter() float64 +} + +type Drawable interface { + Draw() +} + +// Interface composition +type DrawableShape interface { + Shape + Drawable +} + +// Implement structs +type Rectangle struct { + Width float64 + Height float64 +} + +type Circle struct { + Radius float64 +} + +// Rectangle methods (implements Shape interface) +func (r Rectangle) Area() float64 { + return r.Width * r.Height +} + +func (r Rectangle) Perimeter() float64 { + return 2 * (r.Width + r.Height) +} + +func (r Rectangle) Draw() { + fmt.Printf("Drawing a rectangle: %.1fx%.1f\n", r.Width, r.Height) +} + +// Circle methods (implements Shape interface) +func (c Circle) Area() float64 { + return math.Pi * c.Radius * c.Radius +} + +func (c Circle) Perimeter() float64 { + return 2 * math.Pi * c.Radius +} + +func (c Circle) Draw() { + fmt.Printf("Drawing a circle with radius: %.1f\n", c.Radius) +} + +// Functions that work with interfaces +func printShapeInfo(s Shape) { + fmt.Printf("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter()) +} + +func drawShape(d Drawable) { + d.Draw() +} + +func processDrawableShape(ds DrawableShape) { + ds.Draw() + fmt.Printf("Area: %.2f\n", ds.Area()) +} + +// Empty interface example +func printAnyValue(value interface{}) { + fmt.Printf("Value: %v, Type: %T\n", value, value) +} + +func main() { + fmt.Println("=== INTERFACES DEMONSTRATION ===") + + // Creating instances + rect := Rectangle{Width: 5, Height: 3} + circle := Circle{Radius: 4} + + // Using interfaces + fmt.Println("--- BASIC INTERFACE USAGE ---") + printShapeInfo(rect) + printShapeInfo(circle) + + // Interface variables + fmt.Println("\n--- INTERFACE VARIABLES ---") + var shape Shape + + shape = rect + fmt.Printf("Rectangle - ") + printShapeInfo(shape) + + shape = circle + fmt.Printf("Circle - ") + printShapeInfo(shape) + + // Interface composition + fmt.Println("\n--- INTERFACE COMPOSITION ---") + drawShape(rect) + drawShape(circle) + + processDrawableShape(rect) + processDrawableShape(circle) + + // Type assertion + fmt.Println("\n--- TYPE ASSERTION ---") + shape = rect + + if r, ok := shape.(Rectangle); ok { + fmt.Printf("It's a rectangle: %+v\n", r) + } + + if c, ok := shape.(Circle); ok { + fmt.Printf("It's a circle: %+v\n", c) + } else { + fmt.Println("It's not a circle") + } + + // Type switch + fmt.Println("\n--- TYPE SWITCH ---") + shapes := []Shape{rect, circle, Rectangle{Width: 2, Height: 8}} + + for i, s := range shapes { + fmt.Printf("Shape %d: ", i+1) + switch v := s.(type) { + case Rectangle: + fmt.Printf("Rectangle with width %.1f and height %.1f\n", v.Width, v.Height) + case Circle: + fmt.Printf("Circle with radius %.1f\n", v.Radius) + default: + fmt.Printf("Unknown shape: %T\n", v) + } + } + + // Empty interface + fmt.Println("\n--- EMPTY INTERFACE ---") + printAnyValue(42) + printAnyValue("Hello") + printAnyValue(3.14) + printAnyValue(rect) + printAnyValue([]int{1, 2, 3}) + + // Interface with nil + fmt.Println("\n--- NIL INTERFACES ---") + var nilShape Shape + fmt.Printf("Nil interface: %v (nil: %t)\n", nilShape, nilShape == nil) + + var nilPtr *Rectangle + shape = nilPtr + fmt.Printf("Interface with nil pointer: %v (nil: %t)\n", shape, shape == nil) + + // Checking for nil safely + if shape != nil { + // This would panic if nilPtr methods are called + fmt.Println("Shape is not nil (but underlying value might be)") + } +} diff --git a/13_maps.go b/13_maps.go new file mode 100644 index 0000000..0cd7ca3 --- /dev/null +++ b/13_maps.go @@ -0,0 +1,175 @@ +// 13_maps.go - Demonstrates maps in Go +package main + +import "fmt" + +func main() { + fmt.Println("=== MAPS DEMONSTRATION ===") + + // Creating maps + fmt.Println("--- CREATING MAPS ---") + + // Method 1: Using make + grades := make(map[string]int) + grades["Alice"] = 85 + grades["Bob"] = 92 + grades["Charlie"] = 78 + + // Method 2: Map literal + colors := map[string]string{ + "red": "#FF0000", + "green": "#00FF00", + "blue": "#0000FF", + } + + // Method 3: Empty map literal + inventory := map[string]int{} + inventory["apples"] = 50 + inventory["bananas"] = 30 + + fmt.Printf("Grades: %v\n", grades) + fmt.Printf("Colors: %v\n", colors) + fmt.Printf("Inventory: %v\n", inventory) + + // Accessing map values + fmt.Println("\n--- ACCESSING VALUES ---") + aliceGrade := grades["Alice"] + fmt.Printf("Alice's grade: %d\n", aliceGrade) + + // Safe access with ok pattern + if grade, ok := grades["Diana"]; ok { + fmt.Printf("Diana's grade: %d\n", grade) + } else { + fmt.Println("Diana not found in grades") + } + + // Check if key exists + _, exists := colors["yellow"] + fmt.Printf("Yellow exists in colors: %t\n", exists) + + // Map operations + fmt.Println("\n--- MAP OPERATIONS ---") + + // Adding/updating + grades["Diana"] = 88 + grades["Alice"] = 90 // Update existing + fmt.Printf("Updated grades: %v\n", grades) + + // Deleting + delete(grades, "Bob") + fmt.Printf("After deleting Bob: %v\n", grades) + + // Length + fmt.Printf("Number of students: %d\n", len(grades)) + + // Iterating over maps + fmt.Println("\n--- ITERATING OVER MAPS ---") + + // Iterate over key-value pairs + fmt.Println("Color codes:") + for color, code := range colors { + fmt.Printf(" %s: %s\n", color, code) + } + + // Iterate over keys only + fmt.Println("Students:") + for student := range grades { + fmt.Printf(" %s\n", student) + } + + // Iterate over values only + fmt.Println("Grade values:") + for _, grade := range grades { + fmt.Printf(" %d\n", grade) + } + + // Nested maps + fmt.Println("\n--- NESTED MAPS ---") + students := map[string]map[string]interface{}{ + "Alice": { + "age": 20, + "major": "Computer Science", + "gpa": 3.8, + "active": true, + }, + "Bob": { + "age": 22, + "major": "Mathematics", + "gpa": 3.6, + "active": false, + }, + } + + fmt.Printf("Students data: %v\n", students) + + // Accessing nested map values + if alice, ok := students["Alice"]; ok { + if age, ok := alice["age"]; ok { + fmt.Printf("Alice's age: %v\n", age) + } + if major, ok := alice["major"]; ok { + fmt.Printf("Alice's major: %v\n", major) + } + } + + // Map with struct values + fmt.Println("\n--- MAP WITH STRUCT VALUES ---") + type Person struct { + Name string + Age int + City string + } + + people := map[int]Person{ + 1: {"Alice", 25, "New York"}, + 2: {"Bob", 30, "Los Angeles"}, + 3: {"Charlie", 35, "Chicago"}, + } + + fmt.Printf("People: %v\n", people) + + // Modifying struct in map + person := people[1] + person.Age = 26 + people[1] = person // Need to reassign the whole struct + fmt.Printf("Updated Alice: %v\n", people[1]) + + // Map comparison (maps are not comparable) + fmt.Println("\n--- MAP COMPARISON ---") + map1 := map[string]int{"a": 1, "b": 2} + map2 := map[string]int{"a": 1, "b": 2} + + // This would cause compilation error: + // fmt.Printf("Maps equal: %t\n", map1 == map2) + + // Manual comparison + equal := compareMaps(map1, map2) + fmt.Printf("Maps are equal: %t\n", equal) + + // Zero value of map is nil + fmt.Println("\n--- NIL MAPS ---") + var nilMap map[string]int + fmt.Printf("Nil map: %v (nil: %t)\n", nilMap, nilMap == nil) + fmt.Printf("Length of nil map: %d\n", len(nilMap)) + + // Reading from nil map returns zero value + value := nilMap["key"] + fmt.Printf("Value from nil map: %d\n", value) + + // Writing to nil map would panic + // nilMap["key"] = 1 // This would panic +} + +func compareMaps(map1, map2 map[string]int) bool { + if len(map1) != len(map2) { + return false + } + + for key, value1 := range map1 { + if value2, ok := map2[key]; !ok || value1 != value2 { + return false + } + } + + return true +} diff --git a/14_pointers.go b/14_pointers.go new file mode 100644 index 0000000..2242b03 --- /dev/null +++ b/14_pointers.go @@ -0,0 +1,139 @@ +// 14_pointers.go - Demonstrates pointers in Go +package main + +import "fmt" + +func main() { + fmt.Println("=== POINTERS DEMONSTRATION ===") + + // Basic pointer operations + fmt.Println("--- BASIC POINTER OPERATIONS ---") + + var x int = 42 + var p *int = &x // p points to x + + fmt.Printf("Value of x: %d\n", x) + fmt.Printf("Address of x: %p\n", &x) + fmt.Printf("Value of p (address): %p\n", p) + fmt.Printf("Value pointed to by p: %d\n", *p) + + // Modify value through pointer + *p = 100 + fmt.Printf("After *p = 100, x = %d\n", x) + + // Zero value of pointer is nil + fmt.Println("\n--- NIL POINTERS ---") + var nilPtr *int + fmt.Printf("Nil pointer: %v (nil: %t)\n", nilPtr, nilPtr == nil) + + // Creating pointers using new + fmt.Println("\n--- USING NEW ---") + ptr := new(int) + *ptr = 50 + fmt.Printf("Pointer from new: %p, value: %d\n", ptr, *ptr) + + // Pointers with functions + fmt.Println("\n--- POINTERS WITH FUNCTIONS ---") + a := 10 + b := 20 + + fmt.Printf("Before swap: a=%d, b=%d\n", a, b) + swapByValue(a, b) + fmt.Printf("After swapByValue: a=%d, b=%d\n", a, b) + + swapByPointer(&a, &b) + fmt.Printf("After swapByPointer: a=%d, b=%d\n", a, b) + + // Pointers with structs + fmt.Println("\n--- POINTERS WITH STRUCTS ---") + type Person struct { + Name string + Age int + } + + person1 := Person{"Alice", 25} + fmt.Printf("person1: %+v\n", person1) + + // Pointer to struct + personPtr := &person1 + fmt.Printf("Access via pointer: %+v\n", *personPtr) + + // Go automatically dereferences struct pointers + fmt.Printf("Name via pointer: %s\n", personPtr.Name) + personPtr.Age = 26 + fmt.Printf("After updating age via pointer: %+v\n", person1) + + // Creating struct pointer directly + person2 := &Person{"Bob", 30} + fmt.Printf("person2 (created as pointer): %+v\n", *person2) + + // Pointers with slices + fmt.Println("\n--- POINTERS WITH SLICES ---") + slice1 := []int{1, 2, 3} + slice2 := slice1 // Slices are reference types + + fmt.Printf("slice1: %v\n", slice1) + fmt.Printf("slice2: %v\n", slice2) + + slice2[0] = 10 + fmt.Printf("After modifying slice2[0]: slice1=%v, slice2=%v\n", slice1, slice2) + + // Explicit pointer to slice + slicePtr := &slice1 + (*slicePtr)[1] = 20 + fmt.Printf("After modifying via slicePtr: %v\n", slice1) + + // Pointers with arrays + fmt.Println("\n--- POINTERS WITH ARRAYS ---") + arr1 := [3]int{1, 2, 3} + arr2 := arr1 // Arrays are value types (copied) + + fmt.Printf("arr1: %v\n", arr1) + fmt.Printf("arr2: %v\n", arr2) + + arr2[0] = 10 + fmt.Printf("After modifying arr2[0]: arr1=%v, arr2=%v\n", arr1, arr2) + + // Pointer to array + arrPtr := &arr1 + (*arrPtr)[1] = 20 + fmt.Printf("After modifying via arrPtr: %v\n", arr1) + + // Pointer arithmetic is not allowed in Go + fmt.Println("\n--- POINTER LIMITATIONS ---") + nums := []int{1, 2, 3, 4, 5} + ptr1 := &nums[0] + ptr2 := &nums[1] + + fmt.Printf("ptr1 points to: %d\n", *ptr1) + fmt.Printf("ptr2 points to: %d\n", *ptr2) + + // This would not compile: + // ptr1++ // Pointer arithmetic not allowed + // ptr3 := ptr1 + 1 // Pointer arithmetic not allowed + + // Double pointers + fmt.Println("\n--- DOUBLE POINTERS ---") + value := 42 + ptr3 := &value + doublePtr := &ptr3 + + fmt.Printf("value: %d\n", value) + fmt.Printf("*ptr3: %d\n", *ptr3) + fmt.Printf("**doublePtr: %d\n", **doublePtr) + + **doublePtr = 100 + fmt.Printf("After **doublePtr = 100, value = %d\n", value) +} + +// Function that receives values (won't modify originals) +func swapByValue(x, y int) { + x, y = y, x + fmt.Printf("Inside swapByValue: x=%d, y=%d\n", x, y) +} + +// Function that receives pointers (can modify originals) +func swapByPointer(x, y *int) { + *x, *y = *y, *x + fmt.Printf("Inside swapByPointer: *x=%d, *y=%d\n", *x, *y) +} diff --git a/15_concurrency_patterns.go b/15_concurrency_patterns.go new file mode 100644 index 0000000..2ecf3bd --- /dev/null +++ b/15_concurrency_patterns.go @@ -0,0 +1,260 @@ +// 15_concurrency_patterns.go - Advanced concurrency patterns +package main + +import ( + "fmt" + "math/rand" + "sync" + "time" +) + +func main() { + fmt.Println("=== CONCURRENCY PATTERNS DEMONSTRATION ===") + + // Worker Pool Pattern + fmt.Println("--- WORKER POOL PATTERN ---") + workerPoolDemo() + + // Fan-in Fan-out Pattern + fmt.Println("\n--- FAN-IN FAN-OUT PATTERN ---") + fanInOutDemo() + + // Pipeline Pattern + fmt.Println("\n--- PIPELINE PATTERN ---") + pipelineDemo() + + // WaitGroup Example + fmt.Println("\n--- WAITGROUP EXAMPLE ---") + waitGroupDemo() + + // Mutex Example + fmt.Println("\n--- MUTEX EXAMPLE ---") + mutexDemo() +} + +// Worker Pool Pattern +func workerPoolDemo() { + const numWorkers = 3 + const numJobs = 10 + + jobs := make(chan int, numJobs) + results := make(chan int, numJobs) + + // Start workers + for w := 1; w <= numWorkers; w++ { + go worker(w, jobs, results) + } + + // Send jobs + for j := 1; j <= numJobs; j++ { + jobs <- j + } + close(jobs) + + // Collect results + for r := 1; r <= numJobs; r++ { + result := <-results + fmt.Printf("Result: %d\n", result) + } +} + +func worker(id int, jobs <-chan int, results chan<- int) { + for job := range jobs { + fmt.Printf("Worker %d processing job %d\n", id, job) + time.Sleep(100 * time.Millisecond) // Simulate work + results <- job * 2 + } +} + +// Fan-in Fan-out Pattern +func fanInOutDemo() { + input := make(chan int) + + // Fan-out: distribute work to multiple goroutines + ch1 := make(chan int) + ch2 := make(chan int) + ch3 := make(chan int) + + go fanOut(input, ch1, ch2, ch3) + + // Process in parallel + out1 := process("Process-1", ch1) + out2 := process("Process-2", ch2) + out3 := process("Process-3", ch3) + + // Fan-in: combine results + combined := fanIn(out1, out2, out3) + + // Send some data + go func() { + for i := 1; i <= 6; i++ { + input <- i + time.Sleep(100 * time.Millisecond) + } + close(input) + }() + + // Collect results + for result := range combined { + fmt.Printf("Combined result: %d\n", result) + } +} + +func fanOut(input <-chan int, outputs ...chan<- int) { + go func() { + defer func() { + for _, out := range outputs { + close(out) + } + }() + + for value := range input { + for i, out := range outputs { + select { + case out <- value: + fmt.Printf("Sent %d to output %d\n", value, i+1) + default: + // Non-blocking send + } + } + } + }() +} + +func process(name string, input <-chan int) <-chan int { + output := make(chan int) + go func() { + defer close(output) + for value := range input { + processed := value * value // Square the value + fmt.Printf("%s processed %d -> %d\n", name, value, processed) + output <- processed + time.Sleep(50 * time.Millisecond) + } + }() + return output +} + +func fanIn(inputs ...<-chan int) <-chan int { + output := make(chan int) + var wg sync.WaitGroup + + wg.Add(len(inputs)) + + for _, input := range inputs { + go func(ch <-chan int) { + defer wg.Done() + for value := range ch { + output <- value + } + }(input) + } + + go func() { + wg.Wait() + close(output) + }() + + return output +} + +// Pipeline Pattern +func pipelineDemo() { + // Stage 1: Generate numbers + numbers := generateNumbers(5) + + // Stage 2: Square numbers + squared := squareNumbers(numbers) + + // Stage 3: Sum numbers + sum := sumNumbers(squared) + + fmt.Printf("Pipeline result: %d\n", <-sum) +} + +func generateNumbers(count int) <-chan int { + out := make(chan int) + go func() { + defer close(out) + for i := 1; i <= count; i++ { + out <- i + fmt.Printf("Generated: %d\n", i) + } + }() + return out +} + +func squareNumbers(input <-chan int) <-chan int { + out := make(chan int) + go func() { + defer close(out) + for num := range input { + squared := num * num + out <- squared + fmt.Printf("Squared: %d -> %d\n", num, squared) + } + }() + return out +} + +func sumNumbers(input <-chan int) <-chan int { + out := make(chan int) + go func() { + defer close(out) + sum := 0 + for num := range input { + sum += num + fmt.Printf("Sum so far: %d\n", sum) + } + out <- sum + }() + return out +} + +// WaitGroup Example +func waitGroupDemo() { + var wg sync.WaitGroup + + tasks := []string{"Task-1", "Task-2", "Task-3", "Task-4"} + + for _, task := range tasks { + wg.Add(1) + go func(taskName string) { + defer wg.Done() + fmt.Printf("Starting %s\n", taskName) + time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) + fmt.Printf("Completed %s\n", taskName) + }(task) + } + + fmt.Println("Waiting for all tasks to complete...") + wg.Wait() + fmt.Println("All tasks completed!") +} + +// Mutex Example +func mutexDemo() { + var mutex sync.Mutex + var counter int + var wg sync.WaitGroup + + numGoroutines := 10 + incrementsPerGoroutine := 1000 + + for i := 0; i < numGoroutines; i++ { + wg.Add(1) + go func(id int) { + defer wg.Done() + for j := 0; j < incrementsPerGoroutine; j++ { + mutex.Lock() + counter++ + mutex.Unlock() + } + fmt.Printf("Goroutine %d finished\n", id) + }(i) + } + + wg.Wait() + expected := numGoroutines * incrementsPerGoroutine + fmt.Printf("Expected: %d, Actual: %d, Match: %t\n", expected, counter, expected == counter) +} diff --git a/16_memory_management.go b/16_memory_management.go new file mode 100644 index 0000000..943797a --- /dev/null +++ b/16_memory_management.go @@ -0,0 +1,180 @@ +// 16_memory_management.go - Demonstrates memory management concepts +package main + +import ( + "fmt" + "runtime" + "time" +) + +type LargeStruct struct { + Data [1000000]int // Large array to consume memory + ID int +} + +func main() { + fmt.Println("=== MEMORY MANAGEMENT DEMONSTRATION ===") + + // Show initial memory stats + showMemStats("Initial") + + // Create many objects to trigger garbage collection + fmt.Println("\n--- CREATING LARGE OBJECTS ---") + createLargeObjects() + + showMemStats("After creating objects") + + // Force garbage collection + fmt.Println("\n--- FORCING GARBAGE COLLECTION ---") + runtime.GC() + showMemStats("After manual GC") + + // Memory allocation patterns + fmt.Println("\n--- MEMORY ALLOCATION PATTERNS ---") + demonstrateAllocationPatterns() + + // Stack vs Heap allocation + fmt.Println("\n--- STACK VS HEAP ALLOCATION ---") + demonstrateStackVsHeap() + + // Memory leaks prevention + fmt.Println("\n--- MEMORY LEAKS PREVENTION ---") + demonstrateMemoryLeakPrevention() +} + +func showMemStats(label string) { + var m runtime.MemStats + runtime.ReadMemStats(&m) + + fmt.Printf("%s Memory Stats:\n", label) + fmt.Printf(" Alloc = %d KB", bToKb(m.Alloc)) + fmt.Printf(" TotalAlloc = %d KB", bToKb(m.TotalAlloc)) + fmt.Printf(" Sys = %d KB", bToKb(m.Sys)) + fmt.Printf(" NumGC = %d\n", m.NumGC) +} + +func bToKb(b uint64) uint64 { + return b / 1024 +} + +func createLargeObjects() { + objects := make([]*LargeStruct, 100) + + for i := 0; i < 100; i++ { + objects[i] = &LargeStruct{ID: i} + if i%20 == 0 { + fmt.Printf("Created object %d\n", i) + showMemStats(fmt.Sprintf("Object %d", i)) + } + } + + // Objects will be eligible for GC when function returns + fmt.Printf("Created %d large objects\n", len(objects)) +} + +func demonstrateAllocationPatterns() { + // Slice allocation and growth + var slice []int + + for i := 0; i < 1000; i++ { + slice = append(slice, i) + if i == 0 || i == 1 || i == 2 || i == 4 || i == 8 || i == 16 || i == 32 || i == 64 || i == 128 || i == 256 || i == 512 { + fmt.Printf("Slice length: %d, capacity: %d\n", len(slice), cap(slice)) + } + } + + // Pre-allocating vs growing + fmt.Println("\nPre-allocated slice:") + preAllocated := make([]int, 0, 1000) + for i := 0; i < 1000; i++ { + preAllocated = append(preAllocated, i) + } + fmt.Printf("Pre-allocated - length: %d, capacity: %d\n", len(preAllocated), cap(preAllocated)) +} + +func demonstrateStackVsHeap() { + // Stack allocation (typically) + stackVar := 42 + fmt.Printf("Stack variable address: %p\n", &stackVar) + + // Heap allocation (escapes to heap) + heapVar := createHeapVariable() + fmt.Printf("Heap variable address: %p\n", heapVar) + + // Large local variables might escape to heap + largeArray := [100000]int{} + fmt.Printf("Large array address: %p\n", &largeArray) +} + +func createHeapVariable() *int { + x := 42 + return &x // x escapes to heap because we return its address +} + +func demonstrateMemoryLeakPrevention() { + // Channel without proper cleanup (potential leak) + ch := make(chan int, 100) + + // Good practice: always close channels when done + go func() { + for i := 0; i < 10; i++ { + ch <- i + } + close(ch) // Important: close the channel + }() + + // Consume all values + for value := range ch { + fmt.Printf("Received: %d\n", value) + } + + // Slice memory management + demonstrateSliceMemoryManagement() + + // Goroutine cleanup + demonstrateGoroutineCleanup() +} + +func demonstrateSliceMemoryManagement() { + // Creating a large slice + largeSlice := make([]int, 1000000) + for i := range largeSlice { + largeSlice[i] = i + } + + // Taking a small sub-slice - this keeps the entire backing array in memory + badSubSlice := largeSlice[0:10] + + // Good practice: copy the data if you only need a small portion + goodSubSlice := make([]int, 10) + copy(goodSubSlice, largeSlice[0:10]) + + fmt.Printf("Bad sub-slice length: %d, capacity: %d\n", len(badSubSlice), cap(badSubSlice)) + fmt.Printf("Good sub-slice length: %d, capacity: %d\n", len(goodSubSlice), cap(goodSubSlice)) + + // Now largeSlice can be garbage collected if goodSubSlice is used instead of badSubSlice + _ = goodSubSlice + largeSlice = nil // Help GC by explicitly setting to nil +} + +func demonstrateGoroutineCleanup() { + done := make(chan bool) + + // Start a goroutine with proper cleanup + go func() { + defer func() { + fmt.Println("Goroutine cleanup completed") + done <- true + }() + + // Simulate some work + for i := 0; i < 5; i++ { + fmt.Printf("Goroutine working: %d\n", i) + time.Sleep(100 * time.Millisecond) + } + }() + + // Wait for goroutine to complete + <-done + fmt.Println("Goroutine finished properly") +} diff --git a/GO_PROGRAMS_README.md b/GO_PROGRAMS_README.md new file mode 100644 index 0000000..ca19f16 --- /dev/null +++ b/GO_PROGRAMS_README.md @@ -0,0 +1,288 @@ +# Go Interview Preparation Programs + +This repository contains 16 comprehensive Go programs designed to help you prepare for Go/Golang interviews. Each program demonstrates key concepts and features of the Go programming language. + +## How to Run the Programs + +### Prerequisites +- Go 1.16 or later installed on your system +- Basic understanding of command line/terminal + +### Running Individual Programs +Navigate to the directory containing the Go files and run each program using the `go run` command: + +```bash +# Basic programs +go run 01_hello_world.go +go run 02_data_types.go +go run 03_zero_values.go +go run 04_arrays_slices.go +go run 05_type_conversions.go + +# Advanced concepts +go run 06_error_handling.go +go run 07_goroutines_basic.go +go run 08_channels_basic.go +go run 09_channels_select.go +go run 10_range_keyword.go + +# Object-oriented concepts +go run 11_structs_methods.go +go run 12_interfaces.go +go run 13_maps.go +go run 14_pointers.go + +# Advanced topics +go run 15_concurrency_patterns.go +go run 16_memory_management.go +``` + +### Building Executables +You can also build executable files: + +```bash +# Build a specific program +go build 01_hello_world.go +./01_hello_world + +# Build all programs at once +for file in *.go; do + go build "$file" +done +``` + +## Program Descriptions + +### 1. Hello World (`01_hello_world.go`) +- Basic Go program structure +- Package declaration +- Import statements +- Main function + +**Key Interview Topics:** +- Go program structure +- Package system +- Entry point of Go programs + +### 2. Data Types (`02_data_types.go`) +- All basic data types in Go +- Numeric types (int, float, complex) +- String and rune types +- Boolean type +- Type information using %T + +**Key Interview Topics:** +- Go's type system +- Difference between types +- Type safety + +### 3. Zero Values (`03_zero_values.go`) +- Zero values for all data types +- Composite types zero values +- Nil values for pointers, slices, maps + +**Key Interview Topics:** +- Memory initialization +- Default values +- Nil concept in Go + +### 4. Arrays vs Slices (`04_arrays_slices.go`) +- Fixed-size arrays vs dynamic slices +- Slice operations (append, copy) +- Capacity vs length +- Slicing operations + +**Key Interview Topics:** +- Memory allocation differences +- When to use arrays vs slices +- Slice internals + +### 5. Type Conversions (`05_type_conversions.go`) +- Numeric type conversions +- String conversions +- Type assertions +- Type switches +- Interface{} usage + +**Key Interview Topics:** +- Type safety and conversions +- Interface{} and type assertions +- Runtime type checking + +### 6. Error Handling (`06_error_handling.go`) +- Basic error handling patterns +- Custom error types +- Error wrapping +- Multiple return values + +**Key Interview Topics:** +- Go's approach to error handling +- Error vs exceptions +- Best practices for error handling + +### 7. Basic Goroutines (`07_goroutines_basic.go`) +- Creating goroutines +- Concurrent vs sequential execution +- Anonymous goroutines +- Goroutine lifecycle + +**Key Interview Topics:** +- Concurrency in Go +- Goroutines vs threads +- Go scheduler + +### 8. Basic Channels (`08_channels_basic.go`) +- Unbuffered and buffered channels +- Channel directions (send-only, receive-only) +- Closing channels +- Channel communication patterns + +**Key Interview Topics:** +- Channel types and behavior +- Communication between goroutines +- Channel blocking behavior + +### 9. Channel Select (`09_channels_select.go`) +- Select statement +- Non-blocking operations +- Timeouts with channels +- Multiple channel communication + +**Key Interview Topics:** +- Advanced channel patterns +- Non-blocking I/O +- Timeout handling + +### 10. Range Keyword (`10_range_keyword.go`) +- Range over slices, arrays, maps +- Range over strings (runes) +- Range over channels +- Modifying elements during iteration + +**Key Interview Topics:** +- Iteration patterns in Go +- Range behavior with different types +- Common pitfalls with range + +### 11. Structs and Methods (`11_structs_methods.go`) +- Struct definition and initialization +- Methods with value and pointer receivers +- Struct embedding +- Anonymous structs + +**Key Interview Topics:** +- Object-oriented programming in Go +- Value vs pointer receivers +- Composition over inheritance + +### 12. Interfaces (`12_interfaces.go`) +- Interface definition and implementation +- Interface composition +- Type assertion and type switches +- Empty interface + +**Key Interview Topics:** +- Interface implementation (implicit) +- Polymorphism in Go +- Interface best practices + +### 13. Maps (`13_maps.go`) +- Map creation and operations +- Iterating over maps +- Nested maps +- Map comparison and nil maps + +**Key Interview Topics:** +- Hash table implementation +- Map characteristics +- Memory considerations with maps + +### 14. Pointers (`14_pointers.go`) +- Pointer basics and operations +- Pointers with functions +- Pointers with structs and slices +- Memory addressing + +**Key Interview Topics:** +- Memory management +- Pass by value vs reference +- Pointer arithmetic limitations + +### 15. Concurrency Patterns (`15_concurrency_patterns.go`) +- Worker pool pattern +- Fan-in/Fan-out pattern +- Pipeline pattern +- WaitGroup and Mutex usage + +**Key Interview Topics:** +- Advanced concurrency patterns +- Synchronization primitives +- Real-world concurrency scenarios + +### 16. Memory Management (`16_memory_management.go`) +- Garbage collection +- Memory allocation patterns +- Stack vs heap allocation +- Memory leak prevention + +**Key Interview Topics:** +- Go's garbage collector +- Memory optimization +- Performance considerations + +## Common Interview Questions Covered + +1. **Basic Go Concepts:** + - What is Go and why was it created? + - Go workspace and GOPATH + - Package system and imports + +2. **Data Types and Variables:** + - Go's type system + - Zero values + - Type conversions and assertions + +3. **Control Structures:** + - Range keyword + - Type switches + - Select statements + +4. **Functions and Methods:** + - Function declarations + - Method receivers + - Error handling patterns + +5. **Concurrency:** + - Goroutines vs threads + - Channels and communication + - Synchronization primitives + +6. **Memory Management:** + - Garbage collection + - Pointers and memory allocation + - Performance optimization + +7. **Advanced Topics:** + - Interfaces and polymorphism + - Composition patterns + - Concurrency patterns + +## Tips for Interview Preparation + +1. **Run each program** and understand the output +2. **Modify the code** to see how behavior changes +3. **Practice explaining** the concepts out loud +4. **Focus on the differences** between Go and other languages +5. **Understand the trade-offs** of different approaches +6. **Practice coding** similar programs from scratch + +## Additional Practice + +Try modifying these programs to: +- Add error handling where missing +- Implement additional methods +- Create more complex examples +- Combine concepts from multiple programs +- Optimize for performance + +Good luck with your Go interview preparation! diff --git a/QUICK_REFERENCE.md b/QUICK_REFERENCE.md new file mode 100644 index 0000000..384f17d --- /dev/null +++ b/QUICK_REFERENCE.md @@ -0,0 +1,95 @@ +# Quick Reference Guide - Go Interview Programs + +## Run Commands Summary + +```bash +# Individual programs +go run 01_hello_world.go # Basic Go structure +go run 02_data_types.go # All data types +go run 03_zero_values.go # Zero values concept +go run 04_arrays_slices.go # Arrays vs Slices +go run 05_type_conversions.go # Type conversions & assertions +go run 06_error_handling.go # Error handling patterns +go run 07_goroutines_basic.go # Basic goroutines +go run 08_channels_basic.go # Channel fundamentals +go run 09_channels_select.go # Select statement +go run 10_range_keyword.go # Range iteration +go run 11_structs_methods.go # Structs and methods +go run 12_interfaces.go # Interface implementation +go run 13_maps.go # Map operations +go run 14_pointers.go # Pointer concepts +go run 15_concurrency_patterns.go # Advanced concurrency +go run 16_memory_management.go # Memory management + +# Run all programs interactively +./run_all_programs.sh + +# Test compilation of all programs +./test_compilation.sh +``` + +## Key Concepts Covered + +### Fundamentals +- ✅ Package system and imports +- ✅ Data types and zero values +- ✅ Variables and constants +- ✅ Type system and conversions + +### Data Structures +- ✅ Arrays and slices +- ✅ Maps and their operations +- ✅ Structs and methods +- ✅ Pointers and memory addressing + +### Object-Oriented Programming +- ✅ Structs and methods +- ✅ Interfaces and implementation +- ✅ Composition over inheritance +- ✅ Polymorphism through interfaces + +### Concurrency +- ✅ Goroutines basics +- ✅ Channels and communication +- ✅ Select statements +- ✅ Advanced concurrency patterns +- ✅ Synchronization (WaitGroup, Mutex) + +### Advanced Topics +- ✅ Error handling patterns +- ✅ Memory management +- ✅ Garbage collection +- ✅ Range keyword usage + +### Best Practices +- ✅ Proper error handling +- ✅ Memory leak prevention +- ✅ Concurrency patterns +- ✅ Interface design + +## Interview Tips + +1. **Run each program** and understand the output completely +2. **Practice explaining** concepts while the program runs +3. **Modify programs** to test your understanding +4. **Focus on Go-specific** features like goroutines, channels, interfaces +5. **Understand the philosophy** behind Go's design decisions + +## Common Interview Questions Mapped to Programs + +| Question | Program | +|----------|---------| +| "Explain Go's type system" | 02, 03, 05 | +| "Difference between arrays and slices" | 04 | +| "How does error handling work in Go?" | 06 | +| "Explain goroutines and channels" | 07, 08, 09 | +| "How do interfaces work in Go?" | 12 | +| "Explain Go's memory management" | 16 | +| "What are some concurrency patterns?" | 15 | +| "How do pointers work in Go?" | 14 | + +## Quick Test +Run this to verify everything works: +```bash +./test_compilation.sh && echo "✅ All programs ready for interview prep!" +``` diff --git a/run_all_programs.sh b/run_all_programs.sh new file mode 100755 index 0000000..89ebb08 --- /dev/null +++ b/run_all_programs.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# run_all_programs.sh - Script to run all Go programs sequentially + +echo "=========================================" +echo "Go Interview Preparation Programs" +echo "=========================================" + +programs=( + "01_hello_world.go" + "02_data_types.go" + "03_zero_values.go" + "04_arrays_slices.go" + "05_type_conversions.go" + "06_error_handling.go" + "07_goroutines_basic.go" + "08_channels_basic.go" + "09_channels_select.go" + "10_range_keyword.go" + "11_structs_methods.go" + "12_interfaces.go" + "13_maps.go" + "14_pointers.go" + "15_concurrency_patterns.go" + "16_memory_management.go" +) + +for program in "${programs[@]}"; do + if [ -f "$program" ]; then + echo "" + echo "=========================================" + echo "Running: $program" + echo "=========================================" + go run "$program" + echo "" + echo "Press Enter to continue to next program..." + read -r + else + echo "Warning: $program not found!" + fi +done + +echo "" +echo "=========================================" +echo "All programs completed!" +echo "=========================================" diff --git a/test_compilation.sh b/test_compilation.sh new file mode 100755 index 0000000..4df1702 --- /dev/null +++ b/test_compilation.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +# test_compilation.sh - Script to test if all Go programs compile successfully + +echo "Testing compilation of all Go programs..." +echo "=========================================" + +programs=( + "01_hello_world.go" + "02_data_types.go" + "03_zero_values.go" + "04_arrays_slices.go" + "05_type_conversions.go" + "06_error_handling.go" + "07_goroutines_basic.go" + "08_channels_basic.go" + "09_channels_select.go" + "10_range_keyword.go" + "11_structs_methods.go" + "12_interfaces.go" + "13_maps.go" + "14_pointers.go" + "15_concurrency_patterns.go" + "16_memory_management.go" +) + +success_count=0 +total_count=${#programs[@]} + +for program in "${programs[@]}"; do + if [ -f "$program" ]; then + echo -n "Testing $program... " + if go build "$program" 2>/dev/null; then + echo "✓ OK" + rm -f "${program%.go}" # Remove the compiled binary + ((success_count++)) + else + echo "✗ FAILED" + echo "Compilation errors:" + go build "$program" + fi + else + echo "✗ $program not found!" + fi +done + +echo "" +echo "=========================================" +echo "Compilation Results:" +echo "Success: $success_count/$total_count programs" + +if [ $success_count -eq $total_count ]; then + echo "🎉 All programs compile successfully!" + exit 0 +else + echo "❌ Some programs have compilation errors." + exit 1 +fi