Variables & Types
var x int = 42Explicit variable declarationx := 42Short variable declaration - type inferred, only inside functionsvar x, y int = 1, 2Declare multiple variablesx, y := 1, "hello"Multiple short declarationsconst Pi = 3.14Constant - evaluated at compile timeconst ( A = 1; B = 2 )Grouped constantsconst ( A = iota; B; C )iota - auto-incrementing const: 0, 1, 2int / int8 / int16 / int32 / int64Signed integersuint / uint8 / uint16 / uint32 / uint64Unsigned integersfloat32 / float64Floating pointcomplex64 / complex128Complex numbersbooltrue or falsestringImmutable UTF-8 byte sequencebyteAlias for uint8runeAlias for int32 - represents a Unicode code pointanyAlias for interface{} - accepts any type (Go 1.18+)var p *int = &xPointer - & takes address, * dereferencesp := new(int)Allocate zeroed value and return pointerStrings
"hello"String literal - double quotes, supports escape sequences`raw\nstring`Raw string literal - backticks, no escape processing, can span lines"a" + "b"Concatenationlen(s)Byte length (not character count - use utf8.RuneCountInString for runes)s[0]Byte at index (returns byte, not rune)s[1:4]Byte slicefor i, r := range s { }Iterate over Unicode code points (runes)[]byte(s) / string(b)Convert string to/from byte slice[]rune(s) / string(r)Convert string to/from rune slicefmt.Sprintf("Hello, %s! You are %d.", name, age)Format stringfmt.Sprintf("%v", x)%v - default format; %+v includes field names; %#v is Go syntaxstrings.Contains(s, "sub")True if substring foundstrings.HasPrefix(s, "pre") / strings.HasSuffix(s, "suf")Check prefix / suffixstrings.Index(s, "sub")First byte index of substring (-1 if not found)strings.ToUpper(s) / strings.ToLower(s)Change casestrings.TrimSpace(s) / strings.Trim(s, "!")Strip whitespace / specific charactersstrings.Split(s, ",") / strings.Join(parts, ",")Split / joinstrings.Replace(s, "a", "b", -1)Replace all occurrences (-1 = all)strconv.Itoa(42) / strconv.Atoi("42")Int to string / string to intSlices & Maps
[3]int{1, 2, 3}Array - fixed size, part of the type[]int{1, 2, 3}Slice literal - dynamic, reference to underlying arraymake([]int, 5)Slice of length 5, capacity 5make([]int, 0, 10)Slice of length 0, capacity 10len(s) / cap(s)Length / capacity of slices = append(s, 4)Append element - may allocate new backing arrays = append(s, other...)Append all elements of another slices[1:3]Sub-slice - shares memory with originalcopy(dst, src)Copy elements - returns number copiedfor i, v := range s { }Iterate slice with index and valuefor _, v := range s { }Iterate ignoring indexmap[string]int{"a": 1}Map literalm := make(map[string]int)Create empty mapm["key"] = 1Set valuev := m["key"]Get value - returns zero value if key absentv, ok := m["key"]Safe get - ok is false if key absentdelete(m, "key")Remove keyfor k, v := range m { }Iterate map (order not guaranteed)Control Flow
if x > 0 { }No parentheses around condition; braces requiredif x := f(); x > 0 { }if with init statement - x scoped to the if blockif x > 0 { } else if x < 0 { } else { }if / else if / elseswitch x { case 1: ... case 2, 3: ... default: ... }Switch - no fallthrough by defaultswitch { case x > 0: ... }Expressionless switch - like if/else chainfallthroughExplicitly fall through to next casefor i := 0; i < 10; i++ { }C-style for loopfor x > 0 { }While-style loopfor { }Infinite loopfor i, v := range slice { }Range over slicefor k, v := range m { }Range over mapfor r := range ch { }Range over channel - blocks until closedbreak / continueExit loop / skip iterationbreak label / continue labelBreak / continue to labelled outer loopdefer fn()Schedule fn to run when current function returns - LIFO orderdefer func() { recover() }()Recover from panic in deferred functionFunctions
func add(a, b int) int { return a + b }Function with same-type params shorthandfunc minMax(a, b int) (int, int) { return a, b }Multiple return valuesfunc divide(a, b float64) (float64, error) { }Idiomatic: return value + errorfunc sum(nums ...int) int { }Variadic - nums is []int inside the functionfn(slice...)Unpack slice as variadic argsfunc (result int, err error) namedReturns() { }Named return values - can use bare returnadd := func(a, b int) int { return a + b }Function literal / anonymous functionfunc makeAdder(x int) func(int) int { return func(y int) int { return x + y } }Closure - captures outer variablefunc init() { }init - runs automatically before main, once per packagefunc main() { }Entry point for executable packagesStructs & Interfaces
type Point struct { X, Y float64 }Struct definitionp := Point{X: 1, Y: 2}Struct literal with named fieldsp := Point{1, 2}Struct literal positional - fragile, avoidp.X / p.YAccess struct fieldspp := &Point{1, 2}Pointer to structpp.XGo auto-dereferences pointer for field accessfunc (p Point) String() string { }Value receiver methodfunc (p *Point) Scale(f float64) { }Pointer receiver method - can mutate ptype Animal struct { Name string }\ntype Dog struct { Animal; Breed string }Embedding - Dog inherits Animal fields and methodstype Stringer interface { String() string }Interface - satisfied implicitly (no implements keyword)var s Stringer = MyType{}Assign concrete type to interface variablev, ok := s.(MyType)Type assertion - ok is false if not that typeswitch v := s.(type) { case int: ... case string: ... }Type switchtype Reader interface { Read(p []byte) (n int, err error) }Common io.Reader interface patternvar _ Stringer = (*MyType)(nil)Compile-time interface satisfaction checkGoroutines & Channels
go fn(args)Launch goroutine - lightweight concurrent functiongo func() { ... }()Launch anonymous goroutinech := make(chan int)Unbuffered channel - send blocks until receiver is readych := make(chan int, 10)Buffered channel - send blocks only when buffer is fullch <- 42Send value to channelv := <-chReceive value from channelv, ok := <-chReceive - ok is false if channel closed and emptyclose(ch)Close channel - receivers get zero values after drainfor v := range ch { }Receive until channel is closedselect { case v := <-ch1: ... case ch2 <- v: ... default: ... }select - multiplex channel ops; default makes it non-blockingvar wg sync.WaitGroup\nwg.Add(1)\ngo func() { defer wg.Done(); ... }()\nwg.Wait()WaitGroup - wait for goroutines to finishvar mu sync.Mutex\nmu.Lock()\ndefer mu.Unlock()Mutex - protect shared statevar once sync.Once\nonce.Do(fn)Execute fn exactly once across all goroutinesatomic.AddInt64(&counter, 1)Atomic operation - lock-free incrementError Handling
result, err := doSomething()Idiomatic: check error on every callif err != nil { return err }Propagate error up the call stackerrors.New("something went wrong")Create a simple error valuefmt.Errorf("failed to open %s: %w", path, err)%w wraps the error for unwrappingerrors.Is(err, ErrNotFound)Check if error matches (traverses wrapped chain)errors.As(err, &target)Check if error can be assigned to target typeerrors.Unwrap(err)Return the wrapped error (or nil)type NotFoundError struct { Name string }\nfunc (e *NotFoundError) Error() string { }Custom error type - implement the error interfacepanic("something catastrophic")Panic - unwinds stack, runs deferred functionsrecover()Recover from panic - only useful inside a deferred functionvar ErrNotFound = errors.New("not found")Sentinel error - compare with errors.Is