Files
next.orly.dev/.claude/skills/golang/references/effective-go-summary.md
mleku fa71e9e334 Add Golang skill and reference materials
- Introduced a new skill for Golang, providing comprehensive guidance on writing, debugging, and best practices for Go programming.
- Added reference materials including effective Go guidelines, common patterns, and a quick reference cheat sheet to support users in Go development.
- Created a skill creator guide to assist in developing new skills with structured templates and resource management.
- Implemented scripts for skill initialization and packaging to streamline the skill creation process.
2025-11-04 12:36:36 +00:00

7.7 KiB

Effective Go - Key Points Summary

Source: https://go.dev/doc/effective_go

Formatting

  • Use gofmt to automatically format your code
  • Indentation: use tabs
  • Line length: no strict limit, but keep reasonable
  • Parentheses: Go uses fewer parentheses than C/Java

Commentary

  • Every package should have a package comment
  • Every exported name should have a doc comment
  • Comments should be complete sentences
  • Start comments with the name of the element being described

Example:

// Package regexp implements regular expression search.
package regexp

// Compile parses a regular expression and returns, if successful,
// a Regexp object that can be used to match against text.
func Compile(str string) (*Regexp, error) {

Names

Package Names

  • Short, concise, evocative
  • Lowercase, single-word
  • No underscores or mixedCaps
  • Avoid stuttering (e.g., bytes.Buffer not bytes.ByteBuffer)

Getters/Setters

  • Getter: Owner() not GetOwner()
  • Setter: SetOwner()

Interface Names

  • One-method interfaces use method name + -er suffix
  • Examples: Reader, Writer, Formatter, CloseNotifier

MixedCaps

  • Use MixedCaps or mixedCaps rather than underscores

Semicolons

  • Lexer automatically inserts semicolons
  • Never put opening brace on its own line

Control Structures

If

if err := file.Chmod(0664); err != nil {
    log.Print(err)
    return err
}

Redeclaration

f, err := os.Open(name)
// err is declared here

d, err := f.Stat()
// err is redeclared here (same scope)

For

// Like a C for
for init; condition; post { }

// Like a C while
for condition { }

// Like a C for(;;)
for { }

// Range over array/slice/map/channel
for key, value := range oldMap {
    newMap[key] = value
}

// If you only need the key
for key := range m {
    // ...
}

// If you only need the value
for _, value := range array {
    // ...
}

Switch

  • No automatic fall through
  • Cases can be expressions
  • Can switch on no value (acts like if-else chain)
switch {
case '0' <= c && c <= '9':
    return c - '0'
case 'a' <= c && c <= 'f':
    return c - 'a' + 10
case 'A' <= c && c <= 'F':
    return c - 'A' + 10
}

Type Switch

switch t := value.(type) {
case int:
    fmt.Printf("int: %d\n", t)
case string:
    fmt.Printf("string: %s\n", t)
default:
    fmt.Printf("unexpected type %T\n", t)
}

Functions

Multiple Return Values

func (file *File) Write(b []byte) (n int, err error) {
    // ...
}

Named Result Parameters

  • Named results are initialized to zero values
  • Can be used for documentation
  • Enable naked returns
func ReadFull(r Reader, buf []byte) (n int, err error) {
    for len(buf) > 0 && err == nil {
        var nr int
        nr, err = r.Read(buf)
        n += nr
        buf = buf[nr:]
    }
    return
}

Defer

  • Schedules function call to run after surrounding function returns
  • LIFO order
  • Arguments evaluated when defer executes
func trace(s string) string {
    fmt.Println("entering:", s)
    return s
}

func un(s string) {
    fmt.Println("leaving:", s)
}

func a() {
    defer un(trace("a"))
    fmt.Println("in a")
}

Data

Allocation with new

  • new(T) allocates zeroed storage for new item of type T
  • Returns *T
  • Returns memory address of newly allocated zero value
p := new(int)   // p is *int, points to zeroed int

Constructors and Composite Literals

func NewFile(fd int, name string) *File {
    if fd < 0 {
        return nil
    }
    return &File{fd: fd, name: name}
}

Allocation with make

  • make(T, args) creates slices, maps, and channels only
  • Returns initialized (not zeroed) value of type T (not *T)
make([]int, 10, 100)   // slice: len=10, cap=100
make(map[string]int)   // map
make(chan int, 10)     // buffered channel

Arrays

  • Arrays are values, not pointers
  • Passing array to function copies the entire array
  • Array size is part of its type

Slices

  • Hold references to underlying array
  • Can grow dynamically with append
  • Passing slice passes reference

Maps

  • Hold references to underlying data structure
  • Passing map passes reference
  • Zero value is nil

Printing

  • %v - default format
  • %+v - struct with field names
  • %#v - Go syntax representation
  • %T - type
  • %q - quoted string

Initialization

Constants

  • Created at compile time
  • Can only be numbers, characters, strings, or booleans

init Function

  • Each source file can have init() function
  • Called after package-level variables initialized
  • Used for setup that can't be expressed as declarations
func init() {
    // initialization code
}

Methods

Pointers vs. Values

  • Value methods can be invoked on pointers and values
  • Pointer methods can only be invoked on pointers

Rule: Value methods can be called on both values and pointers, but pointer methods should only be called on pointers (though Go allows calling on addressable values).

type ByteSlice []byte

func (slice ByteSlice) Append(data []byte) []byte {
    // ...
}

func (p *ByteSlice) Append(data []byte) {
    slice := *p
    // ...
    *p = slice
}

Interfaces and Other Types

Interfaces

  • A type implements an interface by implementing its methods
  • No explicit declaration of intent

Type Assertions

value, ok := str.(string)

Type Switches

switch v := value.(type) {
case string:
    // v is string
case int:
    // v is int
}

Generality

  • If a type exists only to implement an interface and will never have exported methods beyond that interface, there's no need to export the type itself

The Blank Identifier

Unused Imports and Variables

import _ "net/http/pprof"  // Import for side effects

Interface Checks

var _ json.Marshaler = (*RawMessage)(nil)

Embedding

Composition, not Inheritance

type ReadWriter struct {
    *Reader  // *bufio.Reader
    *Writer  // *bufio.Writer
}

Concurrency

Share by Communicating

  • Don't communicate by sharing memory; share memory by communicating
  • Use channels to pass ownership

Goroutines

  • Cheap: small initial stack
  • Multiplexed onto OS threads
  • Prefix function call with go keyword

Channels

  • Allocate with make
  • Unbuffered: synchronous
  • Buffered: asynchronous up to buffer size
ci := make(chan int)            // unbuffered
cj := make(chan int, 0)         // unbuffered
cs := make(chan *os.File, 100)  // buffered

Channels of Channels

type Request struct {
    args        []int
    f           func([]int) int
    resultChan  chan int
}

Parallelization

const numCPU = runtime.NumCPU()
runtime.GOMAXPROCS(numCPU)

Errors

Error Type

type error interface {
    Error() string
}

Custom Errors

type PathError struct {
    Op   string
    Path string
    Err  error
}

func (e *PathError) Error() string {
    return e.Op + " " + e.Path + ": " + e.Err.Error()
}

Panic

  • Use for unrecoverable errors
  • Generally avoid in library code

Recover

  • Called inside deferred function
  • Stops panic sequence
  • Returns value passed to panic
func server(workChan <-chan *Work) {
    for work := range workChan {
        go safelyDo(work)
    }
}

func safelyDo(work *Work) {
    defer func() {
        if err := recover(); err != nil {
            log.Println("work failed:", err)
        }
    }()
    do(work)
}

A Web Server Example

package main

import (
    "fmt"
    "log"
    "net/http"
)

type Counter struct {
    n int
}

func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    ctr.n++
    fmt.Fprintf(w, "counter = %d\n", ctr.n)
}

func main() {
    ctr := new(Counter)
    http.Handle("/counter", ctr)
    log.Fatal(http.ListenAndServe(":8080", nil))
}