Menu

Earn Premium with Referrals

Invite your friends and earn Premium rewards through our referral program.

See how it works and start inviting friends.

Part 2: Slices, Maps & Interfaces
GO

Part 2: Slices, Maps & Interfaces

Master the mental models behind slices and maps, append growth, interfaces, methods, and receiver behavior in Go.

1. The slice mental model

A slice is a header: {ptr, len, cap} pointing into a (possibly shared) backing array.

  • s[i] indexes into the header; len(s) = how far it owns; cap(s) = how far the backing array runs.
  • Slicing sharess2 := s[1:3] shares the backing array with s. Mutating s2[0] mutates s[1].
  • copy(dst, src) — copies min(len) elements into dst, not alias.
a := []int{1, 2, 3, 4}
b := a[1:3]        // shares
b[0] = 99
fmt.Println(a)     // [1 99 3 4]

Gotcha: the classic “assign a slice = copy” trap — it copies the header only.

2. append & growing

  • append(s, x) adds to the logical slice; if len == cap it allocates a new backing array (usually 2×), copies, then appends.
  • Always reassign: s = append(s, x).
  • append to a zero-length slice with spare capacity mutates the shared array; exceeding capacity silently detaches.

Interview model: “append returns a new slice that may share; rely on the return value.”

3. Maps — the hash table

  • m := make(map[string]int); m["k"] = 1; v := m["k"]; delete(m, "k") — O(1) average.
  • Zero value: nil map — reading is fine, writes panic.
  • Always use the comma-ok form to check presence:
v, ok := m["k"]   // ok=false if missing
  • Maps are reference-like: passing to a function can mutate the original.
  • Iteration order is randomized — never depend on map ordering.
  • Keys must be comparable (int, string, bool, pointer, struct of comparables); slices/maps/funcs can’t be keys.
  • Struct as key works (map[Point]Tar), valuable for composite key lookups.

Concurrency: map is not safe for concurrent read-write → sync.RWMutex or sync.Map (or concurrent map pattern).

4. Interfaces & satisfaction

  • An interface is satisfied implicitly — no implements keyword; a type satisfies it if it has the required methods.
  • Empty interface any (aka interface{}): holds any value; requires type assertion to extract.
var i any = 42
v, ok := i.(int)   // type assertion, ok=false if not int
switch i.(type) {  // type switch
case int:  ...
}
  • nil interface vs nil pointer in interface — classic trap: assigning a (*MyType)(nil) to an interface yields a non-nil interface (dynamic type set, dynamic value nil). Check with reflection or restructure.
  • Interface holds (concrete type, value) pair — both/even just the type part.

5. Value vs pointer receivers

func (c Counter) Inc()      // value receiver — copy
func (c *Counter) Inc()      // pointer receiver — mutate
  • Value receiver: copy; Inc() can’t change the original.
  • Pointer receiver: mutate original; consistent pointer vs value receiver helps satisfy some interfaces.
  • For a package to satisfy an interface, the receiver set must match (pointer receiver satisfies interface only for pointer type).

Interview checkpoint:

  • Slice header vs backing array; when append shares vs reallocates.
  • map ordering, nil-map, comma-ok.
  • Interface satisfaction; type assertions; the typed-nil trap.
  • Method sets (value vs pointer receiver) and interface compatibility.

My Private Notes

Notes are auto-saved locally to this device.