Intro to Go Programming

Okay, here’s a comprehensive article on “Intro to Go Programming,” aiming for approximately 5000 words and covering a wide range of introductory topics:

Intro to Go Programming: A Comprehensive Guide for Beginners

Go, also known as Golang, is a statically typed, compiled programming language designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson. It is syntactically similar to C, but with memory safety, garbage collection, structural typing, and CSP-style concurrency. Go is an excellent choice for building efficient and scalable systems, particularly in areas like networking, cloud infrastructure, and command-line tools. This guide provides a comprehensive introduction to Go, suitable for beginners with some prior programming experience (in any language).

1. Why Choose Go?

Before diving into the syntax and features, let’s understand why Go might be the right choice for your next project:

  • Performance: Go is compiled to machine code, resulting in fast execution speeds comparable to C and C++. It doesn’t rely on a virtual machine, leading to reduced overhead.
  • Concurrency: Go has built-in concurrency features (goroutines and channels) that make it incredibly easy to write concurrent and parallel programs. This is a major advantage for handling multiple tasks simultaneously, especially in network programming and distributed systems.
  • Simplicity: Go’s syntax is clean and concise, making it relatively easy to learn and read. It avoids many of the complexities found in other languages, leading to faster development and easier maintenance.
  • Strong Standard Library: Go boasts a powerful standard library with built-in support for networking, web development, testing, cryptography, and more. This reduces the need for external dependencies.
  • Garbage Collection: Go manages memory automatically through garbage collection, preventing memory leaks and simplifying development. You don’t have to worry about manual memory allocation and deallocation.
  • Static Typing: Go’s static typing helps catch errors during compilation, leading to more robust and reliable code. The compiler enforces type safety, reducing runtime surprises.
  • Cross-Compilation: Go makes it easy to build executables for different operating systems (Windows, macOS, Linux) and architectures from a single codebase.
  • Growing Community and Ecosystem: Go has a vibrant and active community, providing ample resources, libraries, and support for developers.
  • Backed by Google: Go’s development is supported by Google, ensuring its continued maintenance and evolution. It’s used extensively within Google for critical infrastructure.

2. Setting Up Your Go Environment

To start programming in Go, you need to set up your development environment. This involves installing the Go distribution and configuring your workspace.

  • Download and Install Go:

    1. Visit the official Go website: https://go.dev/dl/
    2. Download the appropriate installer for your operating system (Windows, macOS, or Linux).
    3. Run the installer and follow the on-screen instructions. This typically involves setting the installation path.
  • Verify Installation:

    1. Open a new terminal or command prompt window.
    2. Type go version and press Enter. You should see the installed Go version printed, confirming the installation was successful.
  • Setting up the GOPATH (Prior to Go Modules – Less Common Now):

    • The GOPATH environment variable specifies the location of your Go workspace. This workspace traditionally contains three subdirectories:
      • src: Contains your Go source code files.
      • pkg: Contains compiled package objects.
      • bin: Contains executable binaries.
    • You can set the GOPATH using your operating system’s environment variable settings. A common location is $HOME/go (on Linux/macOS) or %USERPROFILE%\go (on Windows).
  • Go Modules (The Recommended Approach):

    • Go Modules, introduced in Go 1.11, are the modern way to manage dependencies and project structure. They largely eliminate the need for a strict GOPATH setup.
    • To create a new project with Go Modules:
      1. Create a new directory for your project (e.g., myproject).
      2. Navigate into the directory using the terminal (cd myproject).
      3. Run go mod init <module_path>. The <module_path> is typically a URL-like path (e.g., github.com/yourusername/myproject), but it doesn’t need to be a real repository initially. This creates a go.mod file, which tracks your project’s dependencies.
  • Choosing a Text Editor or IDE:

    • While you can use any text editor, an IDE or a text editor with Go support significantly enhances your development experience. Popular choices include:
      • Visual Studio Code (VS Code): With the official Go extension, VS Code provides excellent support for Go, including autocompletion, debugging, and more. (Highly Recommended)
      • GoLand: A dedicated Go IDE from JetBrains (paid, but with a free trial).
      • Sublime Text: With the GoSublime plugin.
      • Atom: With the go-plus package.
      • Vim/Neovim: With plugins like vim-go.

3. Your First Go Program: “Hello, World!”

Let’s write the traditional “Hello, World!” program to get a feel for Go’s basic structure:

“`go
package main

import “fmt”

func main() {
fmt.Println(“Hello, World!”)
}
“`

  • package main: Every Go program starts with a package declaration. package main indicates that this is the entry point of an executable program.
  • import "fmt": This line imports the fmt package, which provides functions for formatted input and output (like printing to the console).
  • func main() { ... }: The main function is where the execution of your program begins. There must be exactly one main function in a package main program.
  • fmt.Println("Hello, World!"): This line calls the Println function from the fmt package to print the string “Hello, World!” to the console, followed by a newline.

How to Run the Program:

  1. Save the code: Save the code in a file named hello.go (the .go extension is essential).
  2. Open a terminal: Navigate to the directory where you saved hello.go using the cd command.
  3. Run the program: Execute the command go run hello.go. This compiles and runs the program, and you should see “Hello, World!” printed on the console.
  4. Build an executable (optional): You can also build an executable file using go build hello.go. This will create an executable file (e.g., hello or hello.exe) that you can run directly without using go run.

4. Basic Go Syntax and Data Types

Let’s explore the fundamental building blocks of Go code:

  • Variables:

    • Declaration: Variables are declared using the var keyword, followed by the variable name, type, and an optional initial value.
      go
      var age int // Declares an integer variable named 'age'
      var name string = "Alice" // Declares a string variable named 'name' and initializes it
      var isReady bool // Declares a boolean variable
    • Short Variable Declaration: Inside a function, you can use the short variable declaration operator := to declare and initialize variables in a single step. The type is inferred from the value.
      go
      count := 10
      message := "Hello"
    • Zero Values: If you declare a variable without an initial value, it is automatically initialized to its “zero value.” This is 0 for numeric types, false for booleans, "" (empty string) for strings, and nil for pointers, interfaces, slices, channels, maps, and functions.
    • Constants: Constants are declared using the const keyword. Their values must be known at compile time and cannot be changed.
      go
      const pi = 3.14159
      const greeting = "Welcome"
  • Data Types:

    • Integer Types:
      • int: Platform-dependent integer (usually 32 or 64 bits).
      • int8, int16, int32, int64: Signed integers with specific sizes.
      • uint, uint8, uint16, uint32, uint64: Unsigned integers (non-negative values).
      • uintptr: An unsigned integer large enough to store a pointer value.
    • Floating-Point Types:
      • float32: 32-bit floating-point number.
      • float64: 64-bit floating-point number (more common).
    • Complex Number Types:
      • complex64: Complex numbers with float32 real and imaginary parts.
      • complex128: Complex numbers with float64 real and imaginary parts.
    • Boolean Type:
      • bool: Represents a truth value (true or false).
    • String Type:
      • string: Represents a sequence of UTF-8 encoded characters. Strings are immutable in Go.
    • Rune Type:
      • rune: Represents a Unicode code point. It is an alias for int32. Used to handle individual characters, especially when dealing with non-ASCII characters.
  • Operators:

    • Arithmetic Operators: +, -, *, /, % (modulo).
    • Comparison Operators: == (equal to), != (not equal to), < (less than), > (greater than), <= (less than or equal to), >= (greater than or equal to).
    • Logical Operators: && (logical AND), || (logical OR), ! (logical NOT).
    • Bitwise Operators: & (bitwise AND), | (bitwise OR), ^ (bitwise XOR), &^ (bit clear – AND NOT), << (left shift), >> (right shift).
    • Assignment Operators: =, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=.
  • Comments:

    • Single-line comments: Start with //.
    • Multi-line comments: Start with /* and end with */.
  • Semicolons: Go automatically inserts semicolons at the end of lines in most cases. You rarely need to write them explicitly, except in a few specific situations (like separating statements on a single line).

5. Control Flow

Control flow statements determine the order in which code is executed.

  • if Statements:
    go
    if condition {
    // Code to execute if the condition is true
    } else if anotherCondition {
    // Code to execute if anotherCondition is true
    } else {
    // Code to execute if no conditions are true
    }

    • Go’s if statements do not require parentheses around the condition.
    • Braces {} are always required, even for single-line blocks.
    • You can declare variables within the if statement’s scope:

      go
      if x := calculateValue(); x > 10 {
      // ...
      }

      * for Loops: Go has only one looping construct: the for loop. It can be used in several ways:
      * C-style for loop:
      go
      for i := 0; i < 10; i++ {
      fmt.Println(i)
      }

      * while loop equivalent:
      go
      sum := 0
      for sum < 100 {
      sum += 10
      }

      * Infinite loop:
      go
      for {
      // Code that runs indefinitely (until a 'break' statement)
      }

      * range keyword (for iterating over collections):
      “`go
      numbers := []int{1, 2, 3, 4, 5}
      for index, value := range numbers {
      fmt.Printf(“Index: %d, Value: %d\n”, index, value)
      }

      // Iterate over a string:
      message := “Hello”
      for index, char := range message {
      fmt.Printf(“Index: %d, Character: %c\n”, index, char)
      }

      //Iterate over a map:
      myMap := map[string]int{“a”: 1, “b”: 2}
      for key, value := range myMap{
      fmt.Printf(“key: %s, value: %d\n”, key, value)
      }
      * **`switch` Statements:**go
      switch value {
      case option1:
      // Code for option1
      case option2, option3: // Multiple values for a single case
      // Code for option2 or option3
      default:
      // Code if no cases match
      }
      ``
      * Go's
      switchstatements are more flexible than in some other languages. Cases don't "fall through" automatically (you don't needbreakstatements at the end of each case).
      * You can use expressions in cases.
      * You can have a
      switchstatement without a value, acting like anif-else if-else` chain.

      go
      switch { // No value specified
      case score >= 90:
      fmt.Println("A")
      case score >= 80:
      fmt.Println("B")
      case score >= 70:
      fmt.Println("C")
      default:
      fmt.Println("D")
      }

      * break and continue:
      * break: Exits the innermost for, switch, or select statement.
      * continue: Skips the remaining statements in the current iteration of a for loop and proceeds to the next iteration.
      * defer statement:
      * defer statement defers the execution of a function call until the surrounding function returns.
      * Deferred calls are pushed onto a stack. When a function returns, its deferred calls are executed in last-in-first-out (LIFO) order.
      * Commonly used for cleanup operations, such as closing files or releasing resources.

    go
    func example() {
    fmt.Println("Starting")
    defer fmt.Println("Deferred 1")
    defer fmt.Println("Deferred 2") // This will be executed first
    fmt.Println("Ending")
    }
    // Output:
    // Starting
    // Ending
    // Deferred 2
    // Deferred 1

6. Functions

Functions are blocks of code that perform a specific task.

  • Declaration:
    go
    func functionName(parameter1 type1, parameter2 type2) returnType {
    // Function body
    return value // If the function has a return type
    }

  • Multiple Return Values: Go functions can return multiple values.
    “`go
    func divide(a, b int) (int, error) {
    if b == 0 {
    return 0, errors.New(“division by zero”)
    }
    return a / b, nil
    }

    func main() {
    result, err := divide(10, 2)
    if err != nil {
    fmt.Println(“Error:”, err)
    } else {
    fmt.Println(“Result:”, result)
    }

    _, err = divide(5, 0) // Ignore the result using blank identifier
    if err != nil{
    fmt.Println(“Error:”, err) // Error will be printed
    }
    }
    “`

  • Named Return Values: You can name the return values in the function signature. This acts like declaring variables at the beginning of the function.
    go
    func rectangleProperties(length, width float64) (area float64, perimeter float64) {
    area = length * width
    perimeter = 2 * (length + width)
    return // "naked" return - returns the current values of area and perimeter
    }

  • Variadic Functions: Functions can accept a variable number of arguments of the same type using the ... syntax.
    “`go
    func sum(numbers …int) int {
    total := 0
    for _, num := range numbers {
    total += num
    }
    return total
    }

    func main() {
    fmt.Println(sum(1, 2, 3)) // Output: 6
    fmt.Println(sum(1, 2, 3, 4, 5)) // Output: 15
    }
    * **Anonymous Functions (Closures):** Functions can be defined without a name (anonymous functions). They can be assigned to variables or passed as arguments to other functions.go
    func main() {
    add := func(x, y int) int {
    return x + y
    }
    fmt.Println(add(5, 3)) // Output: 8
    }
    “`
    * Closures can access variables from their surrounding scope, even after the outer function has returned.

  • Function as parameters:
    “`go
    func apply(numbers []int, operation func(int) int) []int{
    result := make([]int, len(numbers)) //create a slice
    for i, num := range numbers {
    result[i] = operation(num)
    }
    return result
    }

    func main(){
    numbers := []int{1, 2, 3, 4, 5}
    // Using an anonymous function as the operation
    squared := apply(numbers, func(x int) int {
    return x * x
    })
    fmt.Println(squared)

    //Define a named function.
    double := func(x int) int{
        return x * 2
    }
    doubled := apply(numbers, double)
    fmt.Println(doubled)
    

    }

    “`

7. Data Structures

Go provides several built-in data structures for organizing and storing data.

  • Arrays:
    • Fixed-size sequences of elements of the same type.
    • Declared using [size]type.
      go
      var arr [5]int // An array of 5 integers
      arr[0] = 1
      arr[1] = 2
      fmt.Println(arr) // Output: [1 2 0 0 0]
      numbers := [3]string{"Alice", "Bob", "Charlie"} // Array literal
  • Slices:

    • Dynamically sized, flexible views into an underlying array.
    • Declared using []type.
    • Slices are references to a portion of an array. Modifying a slice modifies the underlying array (and any other slices that refer to the same underlying array).
      “`go
      numbers := []int{1, 2, 3, 4, 5} // Slice literal
      slice := numbers[1:4] // Creates a slice from index 1 (inclusive) to 4 (exclusive)
      fmt.Println(slice) // Output: [2 3 4]
      slice[0] = 10 // Modifies the underlying array
      fmt.Println(numbers) // Output: [1 10 3 4 5]

    //Append to slice:
    mySlice := []int{1,2}
    mySlice = append(mySlice, 3, 4)
    fmt.Println(mySlice)

    //Create slice using make() function:
    anotherSlice := make([]int, 5, 10) // type, length, capacity
    fmt.Println(anotherSlice)
    fmt.Println(“Length”, len(anotherSlice)) // length
    fmt.Println(“Capacity”, cap(anotherSlice)) // capacity
    ``
    *
    len(slice)returns the length of the slice.
    *
    cap(slice)returns the capacity of the underlying array (the maximum number of elements the slice can hold without reallocation).
    * The
    append()function adds elements to the end of a slice, potentially reallocating the underlying array if necessary.
    * The
    make()` function is used to create slices with a specified length and capacity.

  • Maps:

    • Unordered collections of key-value pairs. Keys must be of a comparable type (e.g., strings, numbers, etc.), and values can be of any type.
    • Declared using map[keyType]valueType.
      “`go
      ages := map[string]int{
      “Alice”: 30,
      “Bob”: 25,
      }
      ages[“Charlie”] = 40
      fmt.Println(ages) // Output: map[Alice:30 Bob:25 Charlie:40]
      age, ok := ages[“David”] // Check if a key exists. ok is a boolean.
      if ok {
      fmt.Println(“David’s age:”, age)
      } else {
      fmt.Println(“David not found”)
      }
      delete(ages, “Bob”) // Delete a key-value pair

    // Create map using make()
    newMap := make(map[int]string)
    newMap[1] = “One”
    fmt.Println(newMap)
    “`

  • Structs:

    • Composite data types that group together zero or more named values (fields) of different types. Similar to classes in other languages, but without methods directly associated with the struct definition (methods are defined separately).
      “`go
      type Person struct {
      FirstName string
      LastName string
      Age int
      }

    func main() {
    p := Person{FirstName: “Alice”, LastName: “Smith”, Age: 30}
    fmt.Println(p.FirstName) // Access fields using dot notation
    p.Age = 31
    fmt.Println(p)

    // Anonymous struct
    anotherPerson := struct {
        Name string
        City string
    }{
        Name: "Bob",
        City: "New York",
    }
    fmt.Println(anotherPerson)
    

    }
    “`

8. Pointers

Pointers are variables that store the memory address of another variable.

  • Declaration: A pointer type is declared using *type.
    go
    var p *int // Declares a pointer to an integer
  • Address-of Operator (&): The & operator returns the memory address of a variable.
    go
    x := 10
    p = &x // p now holds the address of x
  • Dereference Operator (*): The * operator, when used with a pointer, accesses the value stored at the address pointed to by the pointer.
    go
    fmt.Println(*p) // Prints the value of x (which is 10)
    *p = 20 // Changes the value of x to 20 (through the pointer)
  • new function: Go provides a built-in new function that allocates memory for a variable of a given type and returns a pointer to it.
    go
    ptr := new(int) // Allocate memory for an int and return a pointer
    *ptr = 5
    fmt.Println(*ptr) // Output: 5

9. Methods and Interfaces

  • Methods: Methods are functions associated with a particular type (called the receiver). They allow you to define behavior for your types.

    “`go
    type Rectangle struct {
    Width float64
    Height float64
    }

    // Method with a value receiver
    func (r Rectangle) Area() float64 {
    return r.Width * r.Height
    }

    // Method with a pointer receiver
    func (r Rectangle) Scale(factor float64) {
    r.Width
    = factor
    r.Height *= factor
    }

    func main() {
    rect := Rectangle{Width: 10, Height: 5}
    fmt.Println(“Area:”, rect.Area()) // Call the Area method

    rect.Scale(2) // Call the Scale method (modifies the original rectangle)
    fmt.Println("Scaled Width:", rect.Width)
    fmt.Println("Scaled Height:", rect.Height)
    

    }
    “`
    * Value Receiver: The method operates on a copy of the receiver value. Changes made to the receiver inside the method do not affect the original value.
    * Pointer Receiver: The method operates on the original receiver value (through a pointer). Changes made to the receiver inside the method do affect the original value. Use pointer receivers when you need to modify the receiver or when the receiver is a large struct (to avoid copying).

  • Interfaces: Interfaces define a set of method signatures. A type implicitly implements an interface if it defines all the methods specified by the interface. Interfaces provide a way to achieve polymorphism (the ability to work with different types in a uniform way).
    “`go
    type Shape interface {
    Area() float64
    }

    type Circle struct {
    Radius float64
    }

    func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
    }

    type Square struct {
    Side float64
    }
    func (s Square) Area() float64{
    return s.Side * s.Side
    }

    func printArea(s Shape) {
    fmt.Println(“Area:”, s.Area())
    }

    func main() {
    circle := Circle{Radius: 5}
    square := Square{Side: 4}
    printArea(circle) // Pass a Circle to printArea
    printArea(square)
    }
    ``
    * The
    Shapeinterface defines a single method,Area().
    * Both
    CircleandRectangleimplicitly implement theShapeinterface because they both have anArea()method with the correct signature.
    * The
    printAreafunction accepts any type that implements theShape` interface.

10. Concurrency: Goroutines and Channels

Go’s concurrency features are one of its most powerful aspects.

  • Goroutines: Lightweight, concurrently executing functions. They are similar to threads, but much cheaper to create and manage.

    • Launched using the go keyword followed by a function call.
      “`go
      func sayHello() {
      fmt.Println(“Hello from goroutine!”)
      }

    func main() {
    go sayHello() // Launch sayHello in a separate goroutine
    fmt.Println(“Hello from main goroutine!”)
    time.Sleep(time.Second) // Wait for a short time to allow the goroutine to run
    // Without time.Sleep, main goroutine would exit before sayHello() finish.
    }
    * **Channels:** Channels are typed conduits through which you can send and receive values between goroutines. They provide a way to synchronize goroutines and communicate data safely.
    * Declared using `chan type`.
    go
    ch := make(chan int) // Create a channel that can transmit integers
    ``
    * **Sending:**
    channel <- value(sends a value to the channel).
    * **Receiving:**
    value := <-channel` (receives a value from the channel).
    * Channel operations are blocking. If you try to send to a channel that is full (or has no receiver ready), the sending goroutine will block until space becomes available (or a receiver becomes ready). If you try to receive from an empty channel (or has no sender ready), the receiving goroutine will block until a value is available.

    “`go
    func worker(ch chan int) {
    for i := 0; i < 5; i++ {
    ch <- i // Send a value to the channel
    fmt.Println(“Sent:”, i)
    }
    close(ch) // Close the channel to signal that no more values will be sent
    }

    func main() {
    ch := make(chan int)
    go worker(ch)

    for value := range ch { // Receive values from the channel until it's closed
        fmt.Println("Received:", value)
    }
    

    }
    ``
    * **
    close(channel):** Closes a channel. After a channel is closed, you can no longer send values to it, but you can still receive any values that are already in the channel. Receiving from a closed channel returns the zero value of the channel's type immediately (without blocking). A common pattern is to use afor…range` loop to receive values from a channel until it’s closed.

  • Buffered Channels: Channels can be created with a buffer.

    • ch := make(chan int, 5) // Creates a buffered channel that can hold up to 5 integers.
    • Sending to a buffered channel only blocks when the buffer is full. Receiving only blocks when the buffer is empty.
  • select statement: The select statement lets a goroutine wait on multiple communication operations (sending or receiving on channels). It’s similar to a switch statement, but for channels.

    “`go
    func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(time.Second)
        ch1 <- "Message from ch1"
    }()
    
    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "Message from ch2"
    }()
    
    select {
    case msg1 := <-ch1:
        fmt.Println("Received:", msg1)
    case msg2 := <-ch2:
        fmt.Println("Received:", msg2)
    case <-time.After(3 * time.Second): // Timeout after 3 seconds
        fmt.Println("Timeout")
    }
    

    }

    ``
    * The
    selectstatement chooses one of the cases that is ready to proceed. If multiple cases are ready, it chooses one at random.
    * The
    defaultcase (if present) is executed if none of the other cases are ready.
    *
    time.After()` returns a channel that sends the current time after a specified duration. This is useful for implementing timeouts.

11. Error Handling

Go uses explicit error handling. Functions that can fail typically return an error value as their last return value.

  • The error Interface: The error type is a built-in interface:

    go
    type error interface {
    Error() string
    }

    * Any type that implements the Error() method satisfies the error interface.

  • Creating Errors: You can create errors using the errors.New() function (from the errors package) or the fmt.Errorf() function (from the fmt package).

    “`go
    import (
    “errors”
    “fmt”
    )

    func divide(a, b int) (int, error) {
    if b == 0 {
    return 0, errors.New(“division by zero”)
    // OR
    // return 0, fmt.Errorf(“cannot divide %d by zero”, a)
    }
    return a / b, nil // nil indicates no error
    }
    “`

  • Checking for Errors: You should always check the error return value and handle it appropriately.

    go
    result, err := divide(10, 0)
    if err != nil {
    fmt.Println("Error:", err) // Handle the error (e.g., print an error message, log the error, return an error to the caller)
    } else {
    fmt.Println("Result:", result)
    }

    * panic and recover:
    * panic: panic is a built-in function that stops the ordinary flow of control and begins panicking. When the function F calls panic, execution of F stops, any deferred functions in F are executed normally, and then F returns to its caller.
    * recover: recover is a built-in function that regains control of a panicking goroutine. recover is only useful inside deferred functions. During normal execution, a call to recover will return nil and have no other effect. If the current goroutine is panicking, a call to recover will capture the value given to panic and resume normal execution.

    “`go
    func safeDivide(a, b int) (result int) {
    defer func() {
    if r := recover(); r != nil {
    fmt.Println(“Recovered from panic:”, r)
    result = 0 // Set a default value
    }
    }()

    if b == 0{
        panic("Division by zero!")
    }
    result = a / b
    return
    

    }
    func main(){
    fmt.Println(safeDivide(10, 2))
    fmt.Println(safeDivide(10, 0)) // Division by zero, recover from panic.
    fmt.Println(safeDivide(4, 2))
    }

    “`

12. Packages and Modules

  • Packages: Packages are a way to organize Go code into reusable units. Each .go file belongs to a package.

    • The package declaration at the top of a file specifies the package to which the file belongs.
    • Files in the same directory must belong to the same package.
    • To use code from another package, you import it.
  • Modules: Modules are collections of related Go packages that are versioned together. They are the modern way to manage dependencies in Go.

    • A module is defined by a go.mod file in the root directory of the module.
    • The go.mod file lists the module’s path and its dependencies (other modules).
    • go get: Command to download and install packages and dependencies.

    bash
    go get github.com/gorilla/mux # Example: Get a specific package

    * go mod tidy: Removes unused dependencies and add missing ones.
    * You can create your own modules and packages by organizing your code into directories and using go mod init to create a go.mod file.

13. Testing

Go has built-in support for writing unit tests.

  • Test Files: Test files have names

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top