Skip to content

Latest commit

 

History

History
109 lines (77 loc) · 4.04 KB

idiomatic.md

File metadata and controls

109 lines (77 loc) · 4.04 KB

Writing idiomatic Go code means adhering to the common coding practices and patterns established by the Go community. This not only includes syntax and structure but also the philosophy of simplicity, readability, and efficiency that Go encourages. Here are some must-know Go idioms and practices for writing idiomatic code:

  1. Error Checking: Go requires explicit error checking. It's idiomatic to handle errors immediately after they occur, typically with an if err != nil check.

    if err != nil {
        // handle error
    }
  2. Short Variable Declarations: Use short variable declarations (:=) when defining local variables. It infers the type based on the initializer expression.

    name := "Go Programmer"
  3. Composite Literals: When creating instances of structs, arrays, slices, or maps, use composite literals. This makes the code concise and clear.

    person := Person{Name: "John", Age: 30}
  4. Range for Loops: Use range for iterating over slices, arrays, maps, and channels. It's concise and idiomatic.

    for key, value := range myMap {
        fmt.Println(key, value)
    }
  5. Defer for Cleanup: Use defer for functions that need to be executed at the end of a function, typically for cleanup tasks like closing files or unlocking a mutex.

    file, err := os.Open("file.txt")
    if err != nil {
        // handle error
    }
    defer file.Close()
  6. Interfaces: Define small, focused interfaces, sometimes as small as one method. This promotes a flexible and modular design.

    type Reader interface {
        Read(p []byte) (n int, err error)
    }
  7. Error Wrapping: When forwarding errors, it's common to add context using %w with fmt.Errorf to allow errors to be unwrapped later.

    return fmt.Errorf("failed to process request: %w", err)
  8. Capitalization for Exporting: Use capitalization to control the visibility of identifiers. Capitalized identifiers are exported from the package, while lowercase identifiers are not.

    // Exported
    func PublicFunction() { }
    
    // Unexported
    func privateFunction() { }
  9. Zero Values: Go's design encourages use of zero values as sensible defaults. It's common to rely on the zero value of a type (e.g., 0 for int, "" for string, nil for slices/maps).

    var count int // count is 0
  10. Concurrency with Goroutines and Channels: Use goroutines for concurrency and channels for communication between them. This model encourages clear and simple concurrent designs.

    go func() {
        // concurrent execution
    }()
    ch := make(chan int)
  11. Minimal Interface Implementation: In Go, a type implements an interface by implementing its methods. There's no explicit declaration of intent. This promotes a simple and decoupled design.

    type Stringer interface {
        String() string
    }
    
    func (d MyType) String() string {
        return "My representation"
    }
  12. Avoiding Getter and Setter Methods: If a struct field can be safely exposed, prefer direct access over getter and setter methods, keeping in line with Go's philosophy of simplicity and transparency.

  13. Package Naming: Packages are named with simple, lowercase names. Avoid using underscores or mixedCaps. The package name should suggest its purpose (e.g., net/http, os).

  14. Error Constants: Use typed error constants to represent common error cases. This allows easy error checking and is more efficient than comparing error strings.

    type MyError string
    
    func (e MyError) Error() string { return string(e) }
    
    const ErrNotFound = MyError("not found")

Adopting these idioms helps in writing clean, efficient, and maintainable Go code that feels natural to other Go programmers. It also leverages the strengths of the language and its standard library, promoting good programming practices across the Go ecosystem.