Golang
My notes about Golang
Additional sources for learning and practice:
Go Tooling
arguments and outputs
Text formatting
Error handling
About Go's "guard clauses"
How to output errors
import "errors"
Data Types
What kinds, how to define empty data types
Variable naming conventions
What is nil?
Shallow copies vs deep copies
Empty interface{}
Functions with explanations of syntactic sugar
Return values
functions naming conventions
what empty return statement returns
Anonymous functions
Functions vs Methods (general comparison)
Binding
Functions: free, not attached to any type.
Methods: attached to a receiver type; define that type’s behavior.
Syntax
Functions:
Do(x, y).Methods:
x.Do(y)—reads as “x does Do.”
Interfaces
Functions: do not count toward interface satisfaction.
Methods: method sets determine whether a type implements an interface.
Encapsulation
Functions: can’t directly express “behavior of T.”
Methods: group behavior with data; can operate on unexported fields within the same package, aiding encapsulation and discoverability.
Namespacing/Discovery
Functions: live in package namespace (
pkg.Foo).Methods: live on the type (
t.Foo()), making APIs easier to explore via the type.
Overloading/Dispatch
Go has no function overloading. Methods allow distinct operations with the same name across different types (e.g., many types can have
String()).
Constructors/Helpers
Constructors are functions (e.g.,
NewThing(...)).Behavior is usually methods (e.g.,
thing.Do()).
Rule of thumb:
If it’s an operation conceptually “about” a type, make it a method.
If it’s a general utility or factory that doesn’t need a receiver, make it a function.
Pointers
General description:
Referencing in function signature
Dereferencing
Pointer receiver vs value receiver
Using a pointer receiver (e.g. *Config) is the right choice when:
You want to modify the struct's fields
The struct is large and you want to avoid copying
You want all methods to have a consistent receiver type
Example
Key takeaways:
Calling a pointer-receiver method on a value is allowed; Go uses &value.
Passing a struct by value creates a copy; any mutations (even via pointer-receiver methods on that copy) don’t affect the original.
Passing a pointer lets mutations affect the original.
Another example here just in case.
Using pointers with functions vs methods
Functions:
Not tied to a type.
You pass arguments explicitly (value or pointer).
Don’t participate in interface satisfaction.
Example:
Methods:
Bound to a receiver type (value or pointer).
Called with receiver syntax; Go can auto-take address/deref.
Define a type’s behavior and determine interface satisfaction.
Example:
How to choose between the two:
If it’s behavior of the type (good for interfaces/encapsulation), use methods.
If it’s a general operation not tied to a type, use a function.
Use pointer receivers when the method mutates state or the type is large/contains sync primitives. Use value receivers for small, immutable-like types.
Copies in Go (short and sharp):
Assignment copies values: v2 = v1 makes a shallow copy of the struct’s fields.
Passing by value copies: func f(s T) gets a copy of s. Mutations inside f don’t affect the caller.
Pointer avoids copying: func f(s *T) passes a reference; mutations affect the original.
Shallow copy means referenced fields (like slices, maps, pointers) are copied as descriptors, pointing to the same underlying data. So:
Copying a slice copies its header (ptr, len, cap) but not its backing array. Appending may reallocate and diverge; mutating elements before reallocation affects both “copies.”
Maps are reference types; copying a map variable copies the header pointing to the same map. Mutations affect both.
Concurrency: copying structs with sync.Mutex (as in http.Server) is dangerous—copying a mutex is a bug. Prefer pointers to avoid accidental copies.
Performance: large structs are costly to copy; pointers reduce allocation/copying at the cost of potential shared-state complexity.
Rules of thumb for when to use pointers:
Small, immutable-like data: pass by value.
Large structs, types with interior mutability (mutexes, pools), or you need shared state: use pointers.
Goroutines and channels
Mutexes
Generics
Panic
Last updated